Distinto não trabalhando com o LINQ para 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; } } 

Isso é baseado em um exemplo em “LINQ in Action”. Listagem 4.16.

Isso imprime Jon Skeet duas vezes. Por quê? Eu até tentei replace o método Equals na class Author. Ainda distinto parece não funcionar. o que estou perdendo?

Edit: Eu adicionei == e! = Sobrecarga do operador também. Ainda não ajuda.

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

O LINQ Distinct não é tão inteligente quando se trata de objects personalizados.

Tudo o que ele faz é olhar para sua lista e ver que ela tem dois objects diferentes (não importa se eles têm os mesmos valores para os campos de membros).

Uma solução alternativa é implementar a interface IEquatable, conforme mostrado aqui .

Se você modificar sua class de autor, deve 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; } } 

Experimente como DotNetFiddle

O método Distinct() verifica a igualdade de referência para tipos de referência. Isso significa que ele está procurando literalmente o mesmo object duplicado, e não objects diferentes que contenham os mesmos valores.

Há uma sobrecarga que leva um IEqualityComparer , portanto, você pode especificar lógica diferente para determinar se um determinado object é igual a outro.

Se você deseja que o Author normalmente se comporte como um object normal (ou seja, apenas igualdade de referência), mas para os propósitos de Valores distintos, use um IEqualityComparer . Se você quiser que os objects Author sejam comparados com base nos valores de nome, substitua GetHashCode e Equals ou implemente IEquatable .

Os dois membros na interface IEqualityComparer são Equals e GetHashCode . Sua lógica para determinar se dois objects Author são iguais parece ser se as strings First e Last name forem as mesmas.

 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(); } } 

Outra solução sem implementar IEquatable , Equals e GetHashCode é usar o método LINQs GroupBy e selecionar o primeiro item do 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() executa a comparação de igualdade padrão em objects no enumerável. Se você não tiver substituído Equals() e GetHashCode() , ele usará a implementação padrão no object , que compara as referências.

A solução simples é adicionar uma implementação correta de Equals() e GetHashCode() a todas as classs que participam no gráfico de objects que você está comparando (ou seja, Livro e Autor).

A interface IEqualityComparer é uma conveniência que permite implementar Equals() e GetHashCode() em uma class separada quando você não tem access às partes internas das classs que você precisa comparar, ou se você estiver usando um método diferente de comparação .

Há mais uma maneira de obter valores distintos da lista de tipos de dados definidos pelo usuário:

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

Certamente, ele dará um conjunto distinto de dados

Você substituiu Equals (), mas certifique-se também de replace GetHashCode ()

As respostas acima estão erradas !!! Distinto como indicado no MSDN retorna o Equator padrão, que como declarado A propriedade Default verifica se o tipo T implementa a interface System.IEquatable e, em caso afirmativo, retorna um EqualityComparer que usa essa implementação. Caso contrário, ele retorna um EqualityComparer que usa as substituições de Object.Equals e Object.GetHashCode fornecidos por T

O que significa, contanto que você supere Igual a você está bem.

A razão pela qual você está codificando não está funcionando é porque você checa primeiro nome = = sobrenome.

consulte https://msdn.microsoft.com/library/bb348436(v=vs.100).aspx e https://msdn.microsoft.com/pt-br/library/ms224763(v=vs.100).aspx