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.