2009-09-02 36 views
88
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; 
    } 

} 

Điều này dựa trên một ví dụ trong "LINQ in Action". Liệt kê 4.16.Sự khác biệt không hoạt động với LINQ đối tượng

Điều này in Jon Skeet hai lần. Tại sao? Tôi thậm chí đã cố gắng ghi đè phương thức Equals trong lớp Author. Still Distinct dường như không hoạt động. Tôi đang thiếu gì?

Chỉnh sửa: Tôi cũng đã thêm == và! = Quá tải toán tử. Vẫn không giúp được gì.

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

Trả lời

114

LINQ Đặc biệt không thông minh khi nói đến các đối tượng tùy chỉnh.

Tất cả những gì nó làm là xem danh sách của bạn và thấy rằng nó có hai đối tượng khác nhau (không quan tâm rằng chúng có cùng giá trị cho các trường thành viên).

Một giải pháp thay thế là triển khai giao diện IEquatable như được hiển thị here.

Nếu bạn sửa đổi lớp tác giả của bạn như vậy nó sẽ làm việc.

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

Try it as DotNetFiddle

+11

IEquatable là tốt nhưng không đầy đủ; bạn nên * luôn luôn * thực hiện Object.Equals() và Object.GetHashCode() với nhau; IEquatable .Equals không ghi đè Object.Equals, do đó, điều này sẽ thất bại khi thực hiện so sánh không mạnh mẽ, thường xảy ra trong các khuôn khổ và luôn luôn trong các bộ sưu tập không chung chung. – AndyM

+0

Vì vậy, nó là tốt hơn để sử dụng ghi đè của biệt mà mất IEqualityComparer như Rex M đã gợi ý? Ý tôi là tôi nên làm gì nếu tôi không muốn rơi vào cái bẫy. – Tanmoy

+3

@Tanmoy nó phụ thuộc. Nếu bạn muốn Tác giả bình thường hành xử như một đối tượng bình thường (nghĩa là chỉ bình đẳng tham chiếu) nhưng kiểm tra các giá trị tên cho mục đích Phân biệt, hãy sử dụng IEqualityComparer. Nếu bạn luôn * muốn các đối tượng Tác giả được so sánh dựa trên các giá trị tên, thì hãy ghi đè GetHashCode và Equals hoặc thực hiện IEquatable. –

54

Phương thức Distinct() kiểm tra tính bình đẳng tham chiếu cho các loại tham chiếu. Điều này có nghĩa là nó đang tìm kiếm nghĩa đen cùng một đối tượng trùng lặp, không phải các đối tượng khác nhau có chứa các giá trị giống nhau.

Có một overload chụp IEqualityComparer, vì vậy bạn có thể chỉ định logic khác nhau để xác định xem một đối tượng nhất định có bằng một đối tượng khác hay không.

Nếu bạn muốn Tác giả hoạt động bình thường như đối tượng bình thường, nhưng với mục đích Đánh giá bình đẳng riêng biệt theo giá trị tên, hãy sử dụng IEqualityComparer. Nếu bạn luôn muốn đối tượng Tác giả được so sánh dựa trên giá trị tên, thì ghi đè GetHashCode và bằng hoặc triển khai IEquatable.

Hai thành viên trên giao diện IEqualityComparerEqualsGetHashCode. Logic của bạn để xác định xem hai đối tượng Author có giống nhau không nếu các chuỗi tên Đầu tiên và Cuối cùng giống nhau.

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

Cảm ơn bạn ! Việc thực hiện GetHashCode() của bạn cho tôi thấy những gì tôi vẫn còn thiếu. Tôi đã trả về {pass-in object} .GetHashCode(), không phải {property đang được sử dụng để so sánh} .GetHashCode(). Điều đó tạo ra sự khác biệt và giải thích tại sao tôi vẫn không thành công - hai tham chiếu khác nhau sẽ có hai mã băm khác nhau. – pelazem

9

Bạn đã overriden Equals(), nhưng chắc chắn rằng bạn cũng ghi đè GetHashCode()

+0

+1 để nhấn mạnh GetHashCode(). Không thêm triển khai HashCode cơ sở như trong cơ sở ' ^.GetHashCode() ' – Dani

20

Distinct() thực hiện việc so sánh bình đẳng mặc định trên các đối tượng trong đếm được. Nếu bạn không ghi đè Equals()GetHashCode(), thì nó sẽ sử dụng triển khai mặc định trên object, so sánh tham chiếu.

Giải pháp đơn giản là thêm một thi đúng của Equals()GetHashCode() cho tất cả các lớp học mà tham gia vào đồ thị đối tượng bạn đang so sánh (ví dụ: Sách và Tác giả).

Giao diện IEqualityComparer là sự tiện lợi cho phép bạn triển khai Equals()GetHashCode() trong một lớp riêng biệt khi bạn không có quyền truy cập vào nội bộ của lớp bạn cần so sánh hoặc nếu bạn đang sử dụng phương pháp so sánh khác .

+0

Cảm ơn bạn rất nhiều vì sự hài lòng rực rỡ này về các đối tượng tham gia. – suhyura

33

Một giải pháp khác mà không triển khai IEquatable, EqualsGetHashCode là sử dụng phương pháp LINQs GroupBy và để chọn mục đầu tiên từ 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); 
} 
+1

nó đã giúp tôi, chỉ xem xét hiệu suất, hiện điều này thực hiện ở cùng một tốc độ ?, như xem xét các phương pháp trên? – Biswajeet

+0

đẹp hơn nhiều so với làm phức tạp nó với các phương pháp thực hiện, và nếu sử dụng EF sẽ ủy nhiệm công việc cho máy chủ sql. – Zapnologica

+0

trong khi phương pháp này có thể hoạt động, sẽ có một vấn đề hiệu suất do số lượng các thứ được nhóm – Bellash

5

Các câu trả lời ở trên là sai !!! Riêng biệt như đã nêu trên MSDN trả về Equator mặc định như đã nêu Thuộc tính mặc định kiểm tra xem kiểu T có triển khai giao diện System.IEquatable hay không và nếu có, trả về EqualityComparer sử dụng triển khai thực hiện đó. Nếu không, nó sẽ trả về một EqualityComparer mà sử dụng ghi đè của Object.equals và Object.GetHashCode cung cấp bởi T

Có nghĩa là miễn là bạn overide Equals bạn cũng tốt.

Lý do bạn mã không hoạt động là vì bạn kiểm tra firstname == lastname.

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

7

Có thêm một cách để có được giá trị khác biệt từ danh sách sử dụng kiểu dữ liệu được định nghĩa:

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

Chắc chắn, nó sẽ cung cấp cho bộ dữ liệu riêng biệt

Các vấn đề liên quan