class Program
{
static void Main(string[] args)
{
List<Book> books = new List<Book>
{
new Book
{
Name="C# in Depth",
Authors = new List<Author>
{
new Author
{
FirstName = "Jon", LastName="Skeet"
},
new Author
{
FirstName = "Jon", LastName="Skeet"
},
}
},
new Book
{
Name="LINQ in Action",
Authors = new List<Author>
{
new Author
{
FirstName = "Fabrice", LastName="Marguerie"
},
new Author
{
FirstName = "Steve", LastName="Eichert"
},
new Author
{
FirstName = "Jim", LastName="Wooley"
},
}
},
};
var temp = books.SelectMany(book => book.Authors).Distinct();
foreach (var author in temp)
{
Console.WriteLine(author.FirstName + " " + author.LastName);
}
Console.Read();
}
}
public class Book
{
public string Name { get; set; }
public List<Author> Authors { get; set; }
}
public class Author
{
public string FirstName { get; set; }
public string LastName { get; set; }
public override bool Equals(object obj)
{
return true;
//if (obj.GetType() != typeof(Author)) return false;
//else return ((Author)obj).FirstName == this.FirstName && ((Author)obj).FirstName == this.LastName;
}
}
Ceci est basé sur un exemple dans "LINQ en action". Listing 4.16.
Cela imprime Jon Skeet deux fois. Pourquoi? J'ai même essayé de remplacer la méthode Equals dans la classe Author. Still Distinct ne semble pas fonctionner. Qu'est-ce que je rate?
Edit: J'ai également ajouté == et! = Surcharge d'opérateurs. Toujours pas d'aide.
public static bool operator ==(Author a, Author b)
{
return true;
}
public static bool operator !=(Author a, Author b)
{
return false;
}
la source
IEquatable
(et remplacéEquals
/GetHashCode
) mais aucun de mes points d'arrêt ne se déclenche dans ces méthodes sur un LinqDistinct
?GetHashCode
etEquals
, ils ont été touchés pendant la boucle foreach. En effet, levar temp = books.SelectMany(book => book.Authors).Distinct();
retourne unIEnumerable
, ce qui signifie que la demande n'est pas exécutée tout de suite, elle n'est exécutée que lorsque les données sont utilisées. Si vous souhaitez un exemple de ce déclenchement tout de suite, ajoutez.ToList()
après le.Distinct()
et vous verrez les points d'arrêt dansEquals
etGetHashCode
avant le foreach.La
Distinct()
méthode vérifie l'égalité de référence pour les types de référence. Cela signifie qu'il recherche littéralement le même objet dupliqué, et non des objets différents contenant les mêmes valeurs.Il existe une surcharge qui prend un IEqualityComparer , vous pouvez donc spécifier une logique différente pour déterminer si un objet donné est égal à un autre.
Si vous voulez que Author se comporte normalement comme un objet normal (c'est-à-dire uniquement l'égalité de référence), mais aux fins de l'égalité de contrôle distincte par valeurs de nom, utilisez un IEqualityComparer . Si vous souhaitez toujours que les objets Author soient comparés en fonction des valeurs de nom, remplacez GetHashCode et Equals , ou implémentez IEquatable .
Les deux membres de l'
IEqualityComparer
interface sontEquals
etGetHashCode
. Votre logique pour déterminer si deuxAuthor
objets sont égaux semble être si les chaînes de prénom et de nom sont identiques.la source
Une autre solution sans implémentation
IEquatable
,Equals
etGetHashCode
consiste à utiliser laGroupBy
méthode LINQs et à sélectionner le premier élément de l'IGrouping.la source
.GroupBy(y => new { y.FirstName, y.LastName })
Il existe un autre moyen d'obtenir des valeurs distinctes à partir de la liste des types de données définis par l'utilisateur:
Sûrement, cela donnera un ensemble distinct de données
la source
Distinct()
effectue la comparaison d'égalité par défaut sur les objets de l'énumérable. Si vous n'avez pas remplacéEquals()
etGetHashCode()
, il utilise l'implémentation par défaut onobject
, qui compare les références.La solution simple est d'ajouter une implémentation correcte de
Equals()
etGetHashCode()
à toutes les classes qui participent au graphe d'objets que vous comparez (c'est-à-dire Book et Author).L'
IEqualityComparer
interface est une commodité qui vous permet d'implémenterEquals()
etGetHashCode()
dans une classe distincte lorsque vous n'avez pas accès aux internes des classes que vous devez comparer, ou si vous utilisez une méthode de comparaison différente.la source
Vous avez remplacé Equals (), mais assurez-vous de remplacer également GetHashCode ()
la source
<custom>^base.GetHashCode()
Les réponses ci-dessus sont fausses !!! Distinct comme indiqué sur MSDN renvoie l'équateur par défaut qui, comme indiqué. La propriété Default vérifie si le type T implémente l'interface System.IEquatable et, si tel est le cas, renvoie un EqualityComparer qui utilise cette implémentation. Sinon, il renvoie un EqualityComparer qui utilise les substitutions de Object.Equals et Object.GetHashCode fournis par T
Ce qui signifie que tant que vous remplacez Égal, tout va bien.
La raison pour laquelle votre code ne fonctionne pas est que vous vérifiez firstname == lastname.
voir https://msdn.microsoft.com/library/bb348436(v=vs.100).aspx et https://msdn.microsoft.com/en-us/library/ms224763(v=vs.100).aspx
la source
Vous pouvez utiliser la méthode d'extension sur la liste qui vérifie l'unicité en fonction du hachage calculé. Vous pouvez également modifier la méthode d'extension pour prendre en charge IEnumerable.
Exemple:
Méthode d'extension:
la source
Vous pouvez y parvenir de deux manières:
1. Vous pouvez implémenter l'interface IEquatable comme indiqué Méthode Enumerable.Distinct ou vous pouvez voir la réponse de @ skalb à ce poste
2. Si votre objet n'a pas de clé unique, vous pouvez utiliser la méthode GroupBy pour obtenir une liste d'objets distincts, que vous devez regrouper toutes les propriétés de l'objet et après avoir sélectionné le premier objet.
Par exemple, comme ci-dessous et travaillant pour moi:
La classe MyObject est comme ci-dessous:
3. Si votre objet a une clé unique, vous ne pouvez l'utiliser qu'en groupe.
Par exemple, la clé unique de mon objet est Id.
la source