Distinto no funciona con LINQ to Objects

class Program { static void Main(string[] args) { List books = new List { new Book { Name="C# in Depth", Authors = new List { new Author { FirstName = "Jon", LastName="Skeet" }, new Author { FirstName = "Jon", LastName="Skeet" }, } }, new Book { Name="LINQ in Action", Authors = new List { 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 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; } } 

Esto se basa en un ejemplo en “LINQ in Action”. Listado 4.16.

Esto imprime a Jon Skeet dos veces. ¿Por qué? Incluso he intentado anular el método Equals en la clase de Autor. Still Distinct no parece funcionar. ¿Qué me estoy perdiendo?

Editar: he agregado == y! = Sobrecarga del operador también. Todavía no hay ayuda.

  public static bool operator ==(Author a, Author b) { return true; } public static bool operator !=(Author a, Author b) { return false; } 

LINQ Distinct no es tan inteligente cuando se trata de objetos personalizados.

Todo lo que hace es mirar su lista y ver que tiene dos objetos diferentes (no le importa que tengan los mismos valores para los campos de miembros).

Una solución es implementar la interfaz IEquatable como se muestra aquí .

Si modifica su clase Author así debería funcionar.

 public class Author : IEquatable { public string FirstName { get; set; } public string LastName { get; set; } public bool Equals(Author other) { if (FirstName == other.FirstName && LastName == other.LastName) return true; return false; } public override int GetHashCode() { int hashFirstName = FirstName == null ? 0 : FirstName.GetHashCode(); int hashLastName = LastName == null ? 0 : LastName.GetHashCode(); return hashFirstName ^ hashLastName; } } 

Pruébalo como DotNetFiddle

El método Distinct() comprueba la igualdad de referencia para los tipos de referencia. Esto significa que está buscando literalmente el mismo objeto duplicado, no diferentes objetos que contienen los mismos valores.

Hay una sobrecarga que toma un IEqualityComparer , por lo que puede especificar una lógica diferente para determinar si un objeto dado es igual a otro.

Si desea que el Autor se comporte normalmente como un objeto normal (es decir, solo la igualdad de referencia), pero a los efectos de Diferencia comprobar la igualdad por los valores de nombre, utilice un IEqualityComparer . Si siempre quiere que se comparen los objetos Autor en función de los valores de los nombres, entonces anule GetHashCode e Igual , o implemente IEquatable .

Los dos miembros de la interfaz IEqualityComparer son Equals y GetHashCode . Su lógica para determinar si dos objetos de Author son iguales parece ser si las cadenas de Nombre y Apellido son las mismas.

 public class AuthorEquals : IEqualityComparer { public bool Equals(Author left, Author right) { if((object)left == null && (object)right == null) { return true; } if((object)left == null || (object)right == null) { return false; } return left.FirstName == right.FirstName && left.LastName == right.LastName; } public int GetHashCode(Author author) { return (author.FirstName + author.LastName).GetHashCode(); } } 

Otra solución sin implementar IEquatable , Equals y GetHashCode es usar el método LINQs GroupBy y seleccionar el primer elemento de IGrouping.

 var temp = books.SelectMany(book => book.Authors) .GroupBy (y => y.FirstName + y.LastName ) .Select (y => y.First ()); foreach (var author in temp){ Console.WriteLine(author.FirstName + " " + author.LastName); } 

Distinct() realiza la comparación de igualdad predeterminada en los objetos enumerables. Si no ha reemplazado Equals() y GetHashCode() , entonces utiliza la implementación predeterminada en el object , que compara las referencias.

La solución simple es agregar una implementación correcta de Equals() y GetHashCode() a todas las clases que participan en el gráfico de objetos que está comparando (es decir, Libro y Autor).

La interfaz IEqualityComparer es una conveniencia que le permite implementar Equals() y GetHashCode() en una clase separada cuando no tiene acceso a las partes internas de las clases que necesita comparar, o si está usando un método diferente de comparación .

Hay una forma más de obtener valores distintos de la lista de tipos de datos definidos por el usuario:

 YourList.GroupBy(i => i.Id).Select(i => i.First()).ToList(); 

Sin duda, dará un conjunto distinto de datos

Has reemplazado Equals (), pero asegúrate de anular también GetHashCode ()

¡Las respuestas de arriba están equivocadas! Distinto como se indica en MSDN devuelve el Ecuador por defecto que, como se establece, la propiedad Predeterminada comprueba si el tipo T implementa la interfaz System.IEquatable y, si es así, devuelve un EqualityComparer que usa esa implementación. De lo contrario, devuelve un EqualityComparer que usa las anulaciones de Object.Equals y Object.GetHashCode proporcionado por T

Lo que significa que siempre que superes Igual estás bien.

La razón por la que el código no funciona es porque verifica el nombre == apellido.

vea https://msdn.microsoft.com/library/bb348436(v=vs.100).aspx y https://msdn.microsoft.com/en-us/library/ms224763(v=vs.100).aspx