Alkampfer's Place

Il blog di Gian Maria Ricci
posts - 659, comments - 871, trackbacks - 80

My Links

News

Gian Maria Ricci Mvp Logo CCSVI in Multiple Sclerosis

English Blog

Tag Cloud

Article Categories

Archives

Post Categories

Image Galleries

I miei siti

Siti utili

Un comparatore generico

Lavorando in ottica Domain Model spesso si hanno per le mani liste di oggetti che debbono essere ordinati comparando una qualche proprietà di un oggetto di dominio. Una soluzione possibile è utilizzare NGenerics assieme ad un oggetto IComparer. Sicuramente l'aspetto più noioso è il dovere scrivere un oeggetto IComparer per ogni coppia Tipo-proprieta dei nostri oggetti di dominio. Per questa ragione è consigliabile scrivere una volta per tutta un IComparer che effettua una comparazione tramite reflection, naturalmente cercando di ottimizzare le prestazioni, una prima implementazione potrebbe essere la seguente

public class GenericIComparer<T> : IComparer<T> { private readonly MethodBase methodInfo; internal GenericIComparer(MethodBase methodInfo, Boolean reversed) { this.methodInfo = methodInfo; mReversed = reversed; } public GenericIComparer(String propname) : this(propname, false) { } public GenericIComparer(String propname, bool mReversed) : this(typeof(T).GetProperty(propname).GetGetMethod(), mReversed) { } public bool Reversed { get { return mReversed; } set { mReversed = value; } } private Boolean mReversed = false; #region IComparer Members public int Compare(T x, T y) { IComparable obj1 = (IComparable)methodInfo.Invoke(x, null); IComparable obj2 = (IComparable)methodInfo.Invoke(y, null); Int32 result = (obj1.CompareTo(obj2)); return mReversed ? -result : result; } #endregion }

In questo caso la reflection è utilizzata solamente nel costruttore e per minimizzare la memoria occupata si utilizza direttamente un MethodInfo relativo alla parte Get della proprietà. La classe è inoltre dotata di un costruttore internal che permette di creare un genericIComparer passando direttamente da fuori il methodInfo relativo alla proprietà desiderata, questo è necessario per creare una classe factory che è in grado di ottimizzare ulteriormente l'uso della memoria.

public static class GenericComparerFactory { private readonly static Dictionary<Type, Dictionary<String, RuntimeMethodHandle>> comparers = new Dictionary<Type, Dictionary<string, RuntimeMethodHandle>>(); public static GenericIComparer<T> GetComparer<T>(string propertyName, bool reversed) { //Check if the type array for this comparer was created. if (!comparers.ContainsKey(typeof(T))) comparers.Add(typeof(T), new Dictionary<string, RuntimeMethodHandle>()); if (!comparers[typeof(T)].ContainsKey(propertyName)) comparers[typeof(T)].Add( propertyName, typeof(T).GetProperty(propertyName).GetGetMethod().MethodHandle); return (GenericIComparer<T>) new GenericIComparer<T>( MethodInfo.GetMethodFromHandle(comparers[typeof(T)][propertyName]), reversed); } public static GenericIComparer<T> GetComparer<T>(string propertyName) { return GetComparer<T>(propertyName); } }

La classe in questione tiene in memoria un dictionary per ogni tipo di oggetto che deve essere comparato associandogli un altro dictionary che per ogni nome di proprietà memorizza un RuntimeMethodHandle, che altro non è che una rappresentazione compatta di un MethodInfo, ottenibile dalla proprietà MethodHandle di un oggetto MethodInfo. In questo modo la nostra factory memorizza con un semplice intPtr le informazioni sui methodInfo necessari, ricreando il vero oggetto MethodInfo solamente quando necessario.

Alk.

Print | posted on lunedì 2 luglio 2007 18:57 | Filed Under [ .NET ]

Feedback

Gravatar

# re: Un comparatore generico

Allora, io non uso mai custom collection, a meno che non abbia proprio una necessità particolare, questo perchè lavorando con nhibernate poi mi troverei con qualche problema con il lazy load...ma questo è un altro discorso.

La classe postata serve questa necessità
-) Ho una lista di oggetti accessibile tramite IList<T>. Quì puoi avere anche la tua collection personalizzata, che tanto implementerà IList<T>
-) Voglio ordinarla riguardo una proprietà di T, supponi ad esempio che ho una IList<Customer> e voglio ordinare per Customer.Address
-) Utilizzo un algoritmo di NGenerics (Lo trovi su codeplex) a cui debbo passare un IComparer<T>, a questo punto gli passo un GenericComparerFactory.GetComparer<Customer>("Address", false)

L'unico problema di questo approccio è che purtroppo il nome della proprità da utilizzare lo si passa come stringa. Il vantaggio è che non devo stare a scrivere una classe comparer per ogni proprietà di ogni oggetto per la quale voglio ordinare.

Alk.
11/07/2007 11:13 | Gian Maria
Comments have been closed on this topic.

Powered by:
Powered By Subtext Powered By ASP.NET