Implémentation de IEqualityComparer pour comparer les propriétés arbitraires de toute classe (y compris anonyme)

J’écris cette classe soignée en implémentant IEqualityComparer, de sorte que je puisse simplement lui transmettre tout type anonyme (ou en réalité tout type avec des propriétés) et qu’il comparera automatiquement les types en comparant les valeurs de propriété des types.

public class CompareProperty : IEqualityComparer { private Type type; private PropertyInfo propInfo; private ssortingng _fieldName; public ssortingng fieldName { get; set; } public CompareProperty(ssortingng fieldName) { this.fieldName = fieldName; } public bool Equals(T x, T y) { if (this.type == null) { type = x.GetType(); propInfo = type.GetProperty(fieldName); } object objX = propInfo.GetValue(x, null); object objY = propInfo.GetValue(y, null); return objX.ToSsortingng() == objY.ToSsortingng(); } } 

Je pensais que c’était une bonne petite fonction d’aide que je pouvais utiliser plusieurs fois.

Pour l’utiliser, je dois faire:

 var t = typeof(CompareProperty); var g = t.MakeGenericType(infoType.GetType()); var c = g.GetConstructor(new Type[] {String.Empty.GetType()}); var obj = c.Invoke(new object[] {"somePropertyName"}); 

Assez bien, mais que dois-je faire avec la variable obj qu’elle renvoie?

 someEnumerable.Distinct(obj); 

La surcharge de la fonction d’extension distincte ne l’accepte pas, car elle ne voit pas de type IEqualityComparer, elle ne voit bien sûr qu’un object.

 someEnumerable.Distinct((t) obj); someEnumerable.Distinct(obj as t); 

Cela ne fonctionne pas non plus. Type / Espace de noms non trouvé (souligné en rouge).

Comment puis-je obtenir cette droite?

Je vais d’abord fournir une solution pour les types non anonymes et ensuite l’étendre pour qu’elle fonctionne également pour les types anonymes. Espérons que cela vous aidera à comprendre ce que les gens essayaient de dire en commentant votre question.

Mon “universel” IEqualityComparer<> ressemble à ceci:

 public class PropertyComparer : IEqualityComparer { private readonly PropertyInfo propertyToCompare; public PropertyComparer(ssortingng propertyName) { propertyToCompare = typeof(T).GetProperty(propertyName); } public bool Equals(T x, T y) { object xValue = propertyToCompare.GetValue(x, null); object yValue = propertyToCompare.GetValue(y, null); return xValue.Equals(yValue); } public int GetHashCode(T obj) { object objValue = propertyToCompare.GetValue(obj, null); return objValue.GetHashCode(); } } 

Disons que nous voulons d’abord utiliser le type non anonyme, comme Person :

 public class Person { public int Id { get; set; } public ssortingng FirstName { get; set; } public ssortingng Surname { get; set; } } 

L’utilisation serait assez simple:

 IEnumerable people = ... ; // some database call here var distinctPeople = people.Distinct(new PropertyComparer("FirstName")); 

Comme vous pouvez le constater, pour utiliser PropertyComparer , nous devons spécifier le type (les T ) dont les instances vont être comparées. Quel serait le T lorsqu’il s’agit de types anonymes? Comme ils sont générés à l’exécution, vous ne pouvez pas utiliser le comparateur en créant directement son instance, simplement parce que vous ne connaissez pas le T au moment de la compilation. Au lieu de cela, vous devez utiliser l’ inférence de type pour laisser le compilateur C # déduire T du contexte seul. Pour ce faire, écrivez la méthode d’extension suivante:

 public static class LinqExtensions { public static IEnumerable WithDistinctProperty(this IEnumerable source, ssortingng propertyName) { return source.Distinct(new PropertyComparer(propertyName)); } } 

Maintenant, cela fonctionnera aussi avec les types anonymes:

 var distinctPeople = people .Select(x => new { x.FirstName, x.Surname }) .WithDistinctProperty("FirstName"); 

Tout à coup, il n’est pas nécessaire de spécifier le type exact de la requête, car le compilateur C # est assez intelligent pour l’inférer à partir du contexte (qui, dans ce cas, est fourni à partir du type de paramètre source dans la méthode d’extension).

J’espère que ceci vous aidera.

Ajoutez juste vos propres validations.

 class PropertyComparer : IEqualityComparer { private Func _getProperty; public PropertyComparer(Func predicate) { this._getProperty = predicate; } public bool Equals(T x, T y) { return this._getProperty(x).Equals(_getProperty(y)); } public int GetHashCode(T obj) { return this._getProperty(obj).GetHashCode(); } } 

Classe avec méthode d’extension:

 public static class IEnumerableExtensions { public static IEnumerable DistinctBy (this IEnumerable source, Func predicate) { return source.Distinct(new PropertyComparer(predicate)); } } 

Usage:

 someEnumerable.DistinctBy(i => i.SomeProperty);