J'ai donc une collection d'objets. Le type exact n'est pas important. Je veux en extraire toutes les paires uniques d'une paire de propriétés particulières, ainsi:
myObjectCollection.Select(item=>new
{
Alpha = item.propOne,
Bravo = item.propTwo
}
).Distinct();
Ma question est donc la suivante: Distinct utilisera-t-il dans ce cas l'objet par défaut est égal (ce qui me sera inutile, puisque chaque objet est nouveau) ou peut-il être demandé de faire un autre égal (dans ce cas, les valeurs égales de Alpha et Bravo => instances égales)? Y a-t-il un moyen d'obtenir ce résultat, si cela ne le fait pas?
Lisez l'excellent post de K. Scott Allen ici:
Et l'égalité pour tous ... Types anonymes
La réponse courte (et je cite):
Il s'avère que le compilateur C # remplace Equals et GetHashCode pour les types anonymes. L'implémentation des deux méthodes substituées utilise toutes les propriétés publiques du type pour calculer le code de hachage d'un objet et tester son égalité. Si deux objets du même type anonyme ont toutes les mêmes valeurs pour leurs propriétés, les objets sont égaux.
Il est donc totalement sûr d’utiliser la méthode Distinct () sur une requête renvoyant des types anonymes.
public class DelegateComparer<T> : IEqualityComparer<T>
{
private Func<T, T, bool> _equals;
private Func<T, int> _hashCode;
public DelegateComparer(Func<T, T, bool> equals, Func<T, int> hashCode)
{
_equals= equals;
_hashCode = hashCode;
}
public bool Equals(T x, T y)
{
return _equals(x, y);
}
public int GetHashCode(T obj)
{
if(_hashCode!=null)
return _hashCode(obj);
return obj.GetHashCode();
}
}
public static class Extensions
{
public static IEnumerable<T> Distinct<T>(this IEnumerable<T> items,
Func<T, T, bool> equals, Func<T,int> hashCode)
{
return items.Distinct(new DelegateComparer<T>(equals, hashCode));
}
public static IEnumerable<T> Distinct<T>(this IEnumerable<T> items,
Func<T, T, bool> equals)
{
return items.Distinct(new DelegateComparer<T>(equals,null));
}
}
var uniqueItems=students.Select(s=> new {FirstName=s.FirstName, LastName=s.LastName})
.Distinct((a,b) => a.FirstName==b.FirstName, c => c.FirstName.GetHashCode()).ToList();
Désolé pour le formatage raté plus tôt
Intéressant que cela fonctionne en C # mais pas en VB
Renvoie les 26 lettres:
var MyBet = "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ";
MyBet.ToCharArray()
.Select(x => new {lower = x.ToString().ToLower(), upper = x.ToString().ToUpper()})
.Distinct()
.Dump();
Retours 52 ...
Dim MyBet = "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ"
MyBet.ToCharArray() _
.Select(Function(x) New With {.lower = x.ToString.ToLower(), .upper = x.ToString.ToUpper()}) _
.Distinct() _
.Dump()
J'ai fait un petit test et constaté que si les propriétés sont des types valeur, cela semble fonctionner correctement. S'ils ne sont pas des types valeur, le type doit fournir ses propres implémentations Equals et GetHashCode pour que cela fonctionne. Je pense que les chaînes fonctionneraient.
Vous pouvez créer votre propre méthode Distinct Extension qui prend l'expression lambda. Voici un exemple
Créer une classe qui dérive de l'interface IEqualityComparer
public class DelegateComparer<T> : IEqualityComparer<T>
{
private Func<T, T, bool> _equals;
private Func<T, int> _hashCode;
public DelegateComparer(Func<T, T, bool> equals, Func<T, int> hashCode)
{
_equals= equals;
_hashCode = hashCode;
}
public bool Equals(T x, T y)
{
return _equals(x, y);
}
public int GetHashCode(T obj)
{
if(_hashCode!=null)
return _hashCode(obj);
return obj.GetHashCode();
}
}
Puis créez votre méthode d'extension distincte
public static class Extensions
{
public static IEnumerable<T> Distinct<T>(this IEnumerable<T> items,
Func<T, T, bool> equals, Func<T,int> hashCode)
{
return items.Distinct(new DelegateComparer<T>(equals, hashCode));
}
public static IEnumerable<T> Distinct<T>(this IEnumerable<T> items,
Func<T, T, bool> equals)
{
return items.Distinct(new DelegateComparer<T>(equals,null));
}
}
et vous pouvez utiliser cette méthode pour trouver des objets distincts
var uniqueItems=students.Select(s=> new {FirstName=s.FirstName, LastName=s.LastName})
.Distinct((a,b) => a.FirstName==b.FirstName, c => c.FirstName.GetHashCode()).ToList();
Salut, j'ai le même problème et j'ai trouvé une solution. Vous devez implémenter l'interface IEquatable ou simplement substituer les méthodes (Equals & GetHashCode). Mais ce n'est pas le truc, le truc venant de la méthode GetHashCode. Vous ne devez pas renvoyer le code de hachage de l'objet de votre classe, mais vous devez également renvoyer le hachage de la propriété que vous souhaitez comparer.
public override bool Equals(object obj)
{
Person p = obj as Person;
if ( obj == null )
return false;
if ( object.ReferenceEquals( p , this ) )
return true;
if ( p.Age == this.Age && p.Name == this.Name && p.IsEgyptian == this.IsEgyptian )
return true;
return false;
//return base.Equals( obj );
}
public override int GetHashCode()
{
return Name.GetHashCode();
}
Comme vous le voyez, j'ai une classe appelée personne qui a 3 propriétés (Nom, Age, IsEgyptien "Parce que je suis"). Dans le GetHashCode, j'ai renvoyé le hachage de la propriété Name et non de l'objet Person.
Essayez-le et cela fonctionnera ISA. Merci, Modather Sadik
Pour que cela fonctionne dans VB.NET, vous devez spécifier le mot clé Key
avant chaque propriété du type anonyme, comme suit:
myObjectCollection.Select(Function(item) New With
{
Key .Alpha = item.propOne,
Key .Bravo = item.propTwo
}).Distinct()
Je me débattais avec ceci, je pensais que VB.NET ne supportait pas ce type de fonctionnalité, mais en réalité, il le fait.
Si Alpha
et Bravo
héritent tous deux d'une classe commune, vous pourrez dicter le contrôle d'égalité dans la classe parente en implémentant IEquatable<T>
.
Par exemple:
public class CommonClass : IEquatable<CommonClass>
{
// needed for Distinct()
public override int GetHashCode()
{
return base.GetHashCode();
}
public bool Equals(CommonClass other)
{
if (other == null) return false;
return [equality test];
}
}