[C#] LINQ to Objects에서 작동하지 않는 구별

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 in Action”의 예를 기반으로합니다. 목록 4.16.

이것은 Jon Skeet을 두 번 인쇄합니다. 왜? Author 클래스에서 Equals 메서드를 재정의하려고 시도했습니다. 여전히 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가 일반적으로 일반 객체처럼 동작하도록하려면 (즉, 참조 같음 만), Distinct의 목적으로 이름 값으로 같음을 확인하려면 IEqualityComparer를 사용하십시오 . 항상 Author 개체를 이름 값을 기준으로 비교하려면 GetHashCode 및 Equals재정의 하거나 IEquatable을 구현하십시오 .

IEqualityComparer인터페이스 의 두 멤버 는 EqualsGetHashCode입니다. 두 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, EqualsGetHashCodeLINQs 사용하는 GroupBy방법 및 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);
}


답변

사용자 정의 데이터 유형 목록에서 고유 한 값을 가져 오는 또 다른 방법이 있습니다.

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

확실히, 그것은 별개의 데이터 세트를 제공 할 것입니다


답변

Distinct()열거 형의 개체에 대한 기본 같음 비교를 수행합니다. Equals()및을 재정의하지 않은 경우 GetHashCode()에 기본 구현을 사용합니다.object 참조를 비교 .

간단한 솔루션은 추가하는 것입니다 올바른 구현을 Equals()하고GetHashCode() 있습니다 (예 : 도서 및 저자)를 비교하는 객체 그래프에 참여하는 모든 클래스를.

IEqualityComparer인터페이스를 구현할 수있는 편리 Equals()하고 GetHashCode()별도의 클래스에서 당신이 비교의 다른 방법을 사용하는 경우 비교해야하거나 클래스의 내부에 액세스 할 수없는 경우.


답변

Equals ()를 재정의했지만 GetHashCode ()도 재정의했는지 확인하십시오.


답변

위의 답변이 잘못되었습니다 !!! MSDN에 명시된 Distinct는 명시된대로 기본 Equator를 반환합니다 . Default 속성은 T 유형이 System.IEquatable 인터페이스를 구현하는지 여부를 확인하고, 그렇다면 해당 구현을 사용하는 EqualityComparer를 반환합니다. 그렇지 않으면 T에서 제공하는 Object.Equals 및 Object.GetHashCode의 재정의를 사용하는 EqualityComparer를 반환합니다.

즉, Equals를 오버라이드하는 한 괜찮습니다.

코드가 작동하지 않는 이유는 firstname == lastname을 확인하기 때문입니다.

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