2009-02-12 8 views
133

Vì vậy, tôi có một bộ sưu tập các đối tượng. Loại chính xác không quan trọng. Từ đó tôi muốn trích xuất tất cả các cặp độc đáo của một cặp thuộc tính đặc biệt, thusly:LINQ Chọn khác biệt với các kiểu vô danh

myObjectCollection.Select(item=>new 
           { 
            Alpha = item.propOne, 
            Bravo = item.propTwo 
           } 
       ).Distinct(); 

Vì vậy, câu hỏi của tôi là: biệt trong trường hợp này sẽ sử dụng đối tượng mặc định bằng (mà sẽ là vô ích với tôi, kể từ mỗi đối tượng là mới) hoặc nó có thể được nói để làm một bằng khác nhau (trong trường hợp này, giá trị bằng nhau của Alpha và Bravo => trường hợp bằng nhau)? Có cách nào để đạt được kết quả đó, nếu điều này không làm điều đó?

+0

Đây có phải là LINQ-to-Objects hoặc LINQ-to-SQL không? Nếu chỉ là đồ vật, có thể bạn sẽ không may mắn. Tuy nhiên, nếu L2S, thì nó có thể hoạt động, vì DISTINCT sẽ được chuyển vào câu lệnh SQL. –

Trả lời

163

Có một chi qua đường bưu tuyệt vời K. Scott Allen ở đây:

And Equality for All ... Anonymous Types

Câu trả lời ngắn (và tôi trích dẫn):

Hóa ra các biên dịch C# sẽ ghi đè Equals và GetHashCode cho các loại vô danh . Việc triển khai hai phương pháp được ghi đè sử dụng tất cả các thuộc tính công khai thuộc loại này để tính mã băm của đối tượng và thử nghiệm cho bình đẳng. Nếu hai đối tượng cùng loại ẩn danh có cùng giá trị cho thuộc tính của chúng - các đối tượng đều bằng nhau.

Vì vậy, hoàn toàn an toàn khi sử dụng phương thức Distinct() trên truy vấn trả về các loại ẩn danh.

+1

Điều này chỉ đúng, tôi nghĩ, nếu bản thân các thuộc tính là các loại giá trị hoặc thực hiện bình đẳng giá trị - hãy xem câu trả lời của tôi. – tvanfosson

+0

Có, vì nó sử dụng GetHashCode trên mỗi thuộc tính sau đó nó sẽ chỉ hoạt động nếu mỗi thuộc tính có thực hiện duy nhất của riêng nó. Tôi nghĩ rằng hầu hết các trường hợp sử dụng sẽ chỉ liên quan đến các loại đơn giản như các thuộc tính vì vậy nó thường an toàn. –

+3

Điều đó có nghĩa là sự bình đẳng của hai loại ẩn danh phụ thuộc vào sự bình đẳng của các thành viên, điều này là tốt cho tôi, vì các thành viên được định nghĩa ở đâu đó mà tôi có thể nhận và bỏ qua sự bình đẳng nếu tôi phải. Tôi chỉ không muốn phải tạo ra một lớp cho điều này chỉ để ghi đè bằng. – GWLlosa

0

Nếu AlphaBravo cả hai được kế thừa từ một lớp chung, bạn sẽ có thể ra lệnh kiểm tra bình đẳng trong lớp cha bằng cách triển khai IEquatable<T>.

Ví dụ:

public class CommonClass : IEquatable<CommonClass> 
{ 
    // needed for Distinct() 
    public override int GetHashCode() 
    { 
     return base.GetHashCode(); 
    } 

    public bool Equals(CommonClass other) 
    { 
     if (other == null) return false; 
     return [equality test]; 
    } 
} 
+0

vì vậy nếu bạn sử dụng như các thuộc tính của các lớp vô danh thực hiện IEquatable , Equals được gọi thay vì hành vi mặc định (kiểm tra tất cả các thuộc tính công khai thông qua sự phản ánh?) –

3

Tôi chạy một thử nghiệm nhỏ và thấy rằng nếu tài sản là loại giá trị, nó dường như làm việc ok. Nếu chúng không phải là các kiểu giá trị, thì kiểu cần cung cấp các hàm Equals và GetHashCode riêng của nó để nó hoạt động. Strings, tôi nghĩ, sẽ hoạt động.

2

Bạn có thể tạo phương thức Tiện ích riêng biệt của riêng bạn, có biểu thức lambda.Dưới đây là một ví dụ

Tạo một lớp mà bắt nguồn từ giao diện IEqualityComparer

public class DelegateComparer<T> : IEqualityComparer<T> 
{ 
    private Func<T, T, bool> _equals; 
    private Func<T, int> _hashCode; 
    public DelegateComparer(Func<T, T, bool> equals, Func<T, int> hashCode) 
    { 
     _equals= equals; 
     _hashCode = hashCode; 
    } 
    public bool Equals(T x, T y) 
    { 
     return _equals(x, y); 
    } 

    public int GetHashCode(T obj) 
    { 
     if(_hashCode!=null) 
      return _hashCode(obj); 
     return obj.GetHashCode(); 
    }  
} 

Sau đó tạo phương pháp mở rộng riêng biệt của bạn

public static class Extensions 
{ 
    public static IEnumerable<T> Distinct<T>(this IEnumerable<T> items, 
     Func<T, T, bool> equals, Func<T,int> hashCode) 
    { 
     return items.Distinct(new DelegateComparer<T>(equals, hashCode));  
    } 
    public static IEnumerable<T> Distinct<T>(this IEnumerable<T> items, 
     Func<T, T, bool> equals) 
    { 
     return items.Distinct(new DelegateComparer<T>(equals,null)); 
    } 
} 

và bạn có thể sử dụng phương pháp này tìm các mục riêng biệt

var uniqueItems=students.Select(s=> new {FirstName=s.FirstName, LastName=s.LastName}) 
      .Distinct((a,b) => a.FirstName==b.FirstName, c => c.FirstName.GetHashCode()).ToList(); 
+0

Phần mở rộng này không thể xử lý kiểu 'đối tượng' và' đối tượng'. Nếu cả hai 'đối tượng' là' chuỗi' nó vẫn trả về các hàng trùng lặp. Hãy thử 'FirstName' là typeof' object' và gán với cùng 'chuỗi' ở đó. – CallMeLaNN

14
public class DelegateComparer<T> : IEqualityComparer<T> 
{ 
    private Func<T, T, bool> _equals; 
    private Func<T, int> _hashCode; 
    public DelegateComparer(Func<T, T, bool> equals, Func<T, int> hashCode) 
    { 
     _equals= equals; 
     _hashCode = hashCode; 
    } 
    public bool Equals(T x, T y) 
    { 
     return _equals(x, y); 
    } 

    public int GetHashCode(T obj) 
    { 
     if(_hashCode!=null) 
      return _hashCode(obj); 
     return obj.GetHashCode(); 
    }  
} 

public static class Extensions 
{ 
    public static IEnumerable<T> Distinct<T>(this IEnumerable<T> items, 
     Func<T, T, bool> equals, Func<T,int> hashCode) 
    { 
     return items.Distinct(new DelegateComparer<T>(equals, hashCode));  
    } 
    public static IEnumerable<T> Distinct<T>(this IEnumerable<T> items, 
     Func<T, T, bool> equals) 
    { 
     return items.Distinct(new DelegateComparer<T>(equals,null)); 
    } 
} 

var uniqueItems=students.Select(s=> new {FirstName=s.FirstName, LastName=s.LastName}) 
      .Distinct((a,b) => a.FirstName==b.FirstName, c => c.FirstName.GetHashCode()).ToList(); 
.210

Xin lỗi vì sự sai lầm định dạng trước đó

+0

Tiện ích mở rộng này không thể xử lý loại 'đối tượng' và' đối tượng'.Nếu cả hai 'đối tượng' là' chuỗi' nó vẫn trả về các hàng trùng lặp. Hãy thử 'FirstName' là typeof' object' và gán với cùng 'chuỗi' ở đó. – CallMeLaNN

+0

Đây là một câu trả lời tuyệt vời cho các đối tượng đã nhập nhưng không cần thiết cho các loại ẩn danh. – crokusek

4

Thú vị rằng nó hoạt động trong C# nhưng không có trong VB

Trả về 26 chữ cái:

var MyBet = "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ"; 
MyBet.ToCharArray() 
.Select(x => new {lower = x.ToString().ToLower(), upper = x.ToString().ToUpper()}) 
.Distinct() 
.Dump(); 

Returns 52 ...

Dim MyBet = "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ" 
MyBet.ToCharArray() _ 
.Select(Function(x) New With {.lower = x.ToString.ToLower(), .upper = x.ToString.ToUpper()}) _ 
.Distinct() _ 
.Dump() 
+9

Nếu bạn thêm từ khóa 'Khóa' vào loại ẩn danh,' .Distinct() 'sẽ hoạt động như dự định (ví dụ:' Mới với {Khóa .lower = x.ToString.ToLower(), Khóa .upper = x.ToString. ToUpper()} '). –

+3

Cory là đúng. Bản dịch chính xác của mã C# 'new {A = b} 'là' New {Key .A = b} '. Các thuộc tính không khóa trong các lớp VB ẩn danh có thể thay đổi được, đó là lý do tại sao chúng được so sánh bằng tham chiếu. Trong C#, tất cả các thuộc tính của các lớp ẩn danh đều không thay đổi. – Heinzi

0

Hey có tôi đã nhận được cùng một vấn đề và tôi tìm thấy một giải pháp. Bạn phải triển khai giao diện IEquatable hoặc chỉ cần ghi đè các phương thức (Bằng & GetHashCode). Nhưng đây không phải là thủ thuật, thủ thuật trong phương thức GetHashCode. Bạn không nên trả về mã băm của đối tượng lớp của bạn, nhưng bạn nên trả về giá trị băm của thuộc tính mà bạn muốn so sánh như thế.

public override bool Equals(object obj) 
    { 
     Person p = obj as Person; 
     if (obj == null) 
      return false; 
     if (object.ReferenceEquals(p , this)) 
      return true; 
     if (p.Age == this.Age && p.Name == this.Name && p.IsEgyptian == this.IsEgyptian) 
      return true; 
     return false; 
     //return base.Equals(obj); 
    } 
    public override int GetHashCode() 
    { 
     return Name.GetHashCode(); 
    } 

Như bạn thấy tôi có một lớp được gọi là người nhận được 3 tài sản (Tên, Tuổi, IsEgyptian "Because I am") Trong GetHashCode tôi trở lại các hash của thuộc tính Name không phải là đối tượng Person.

Hãy thử và nó sẽ hoạt động ISA. Cảm ơn bạn, Modather Sadik

+1

GetHashCode nên sử dụng tất cả các trường và thuộc tính giống nhau được sử dụng trong so sánh cho bình đẳng, không chỉ một trong số chúng. tức là 'public override int GetHashCode() {return this.Name.GetHashCode()^this.Age.GetHashCode()^this.IsEgyptian.GetHashCode(); } ' –

+0

Để biết thông tin về cách tạo thuật toán băm tốt: http://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-an-overridden-system-object-gethashcode –