Вопрос

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

}

Это основано на примере из "LINQ в действии".Листинг 4.16.

Здесь Джон Скит печатается дважды.Почему?Я даже пробовал переопределять метод Equals в классе Author .По-прежнему Distinct, похоже, не работает.Что я упускаю из виду?

Редактировать:Я также добавил перегрузку оператора == и !=.По-прежнему никакой помощи.

 public static bool operator ==(Author a, Author b)
    {
        return true;
    }
    public static bool operator !=(Author a, Author b)
    {
        return false;
    }
Это было полезно?

Решение

LINQ Distinct не настолько умен, когда дело доходит до пользовательских объектов.

Все, что он делает, это просматривает ваш список и видит, что в нем есть два разных объекта (его не волнует, что они имеют одинаковые значения для полей-членов).

Одним из обходных путей является реализация интерфейса IEquatable, как показано на рисунке здесь.

Если вы измените свой класс Author таким образом, это должно сработать.

public class Author : IEquatable<Author>
{
    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;
    }
}

Попробуйте это как DotNetFiddle

Другие советы

В Distinct() метод проверяет равенство ссылок для ссылочных типов.Это означает, что он ищет буквально один и тот же дублированный объект, а не разные объекты, которые содержат одинаковые значения.

Существует перегрузка который требует Сравнитель IEqualityComparer, таким образом, вы можете указать другую логику для определения того, равен ли данный объект другому.

Если вы хотите, чтобы Author обычно вел себя как обычный объект (т.е.только ссылочное равенство), но для целей отдельной проверки равенства по значениям имен используйте Сравнитель IEqualityComparer.Если вы всегда хотите, чтобы объекты Author сравнивались на основе значений name, то переопределить GetHashCode и Equals, или реализовать IEquatable.

Два участника на IEqualityComparer интерфейс - это Equals и GetHashCode.Ваша логика для определения того, являются ли два Author объекты кажутся равными, если строки имени и фамилии совпадают.

public class AuthorEquals : IEqualityComparer<Author>
{
    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();
    }
}

Другое решение без реализации IEquatable, Equals и GetHashCode заключается в использовании LINQs GroupBy метод и выбрать первый элемент из игрогруппировки.

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

Существует еще один способ получить различные значения из списка пользовательского типа данных:

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

Конечно, это даст отличный набор данных

Distinct() выполняет сравнение равенства по умолчанию для объектов в перечислимом.Если вы еще не переопределили Equals() и GetHashCode(), затем он использует реализацию по умолчанию на object, который сравнивает ссылки.

Простое решение состоит в том, чтобы добавить правильный внедрение Equals() и GetHashCode() ко всем классам, которые участвуют в графе объектов, который вы сравниваете (т.Е. Book и Author).

В IEqualityComparer интерфейс - это удобство, которое позволяет вам реализовать Equals() и GetHashCode() в отдельном классе, когда у вас нет доступа к внутренним элементам классов, которые вам нужно сравнить, или если вы используете другой метод сравнения.

Вы переопределили Equals(), но убедитесь, что вы также переопределили GetHashCode()

Приведенные выше ответы неверны!!!Distinct, как указано в MSDN, возвращает Экватор по умолчанию, который, как указано Свойство по умолчанию проверяет, реализует ли тип T систему.Интерфейс IEquatable, и, если это так, возвращает EqualityComparer, который использует эту реализацию. В противном случае он возвращает EqualityComparer, который использует переопределения Object.Equals и Object.GetHashCode, предоставленный T

Это означает, что до тех пор, пока вы переигрываете, с вами все в порядке.

Причина, по которой ваш код не работает, заключается в том, что вы проверяете firstname==lastname .

видишь https://msdn.microsoft.com/library/bb348436 (v= против100).aspx и https://msdn.microsoft.com/en-us/library/ms224763 (v= против100).aspx

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top