2011-01-05 21 views
42

Tôi có một biệt() tuyên bố LINQ sử dụng comparer tùy chỉnh của riêng tôi, như thế này:Sử dụng một đại biểu cho comparer bình đẳng cho LINQ của biệt()

class MyComparer<T> : IEqualityComparer<T> where T : MyType 
{ 
    public bool Equals(T x, T y) 
    { 
     return x.Id.Equals(y.Id); 
    } 

    public int GetHashCode(T obj) 
    { 
     return obj.Id.GetHashCode(); 
    } 
} 

... 

var distincts = bundle.GetAllThings.Distinct(new MyComparer<MySubType>()); 

Đây là tất cả tiền phạt và dandy và làm việc như tôi muốn. Trong sự tò mò, tôi có cần phải xác định Comparer của riêng tôi, hoặc tôi có thể thay thế nó bằng một đại biểu? Tôi nghĩ tôi có thể làm một việc như sau:

var distincts = bundle.GetAllThings.Distinct((a,b) => a.Id == b.Id); 

Nhưng điều này không biên dịch. Có thủ thuật gọn gàng không?

+0

Bạn nên có một 'ReferenceEquals' kiểm tra chống lại null trên 'x' và' y' trong' thực hiện Equals' của bạn. – nicodemus13

Trả lời

98

Riêng biệt lấy IEqualityComparer làm đối số thứ hai, vì vậy bạn sẽ cần trình duyệt IEqualityComparer. Nó không phải là quá khó để thực hiện một chung chung mà sẽ mất một đại biểu, mặc dù. Tất nhiên, điều này có thể đã được triển khai ở một số nơi, chẳng hạn như MoreLINQ đề xuất trong một trong các câu trả lời khác.

Bạn có thể thực hiện nó một cái gì đó như thế này:

public static class Compare 
{ 
    public static IEnumerable<T> DistinctBy<T, TIdentity>(this IEnumerable<T> source, Func<T, TIdentity> identitySelector) 
    { 
     return source.Distinct(Compare.By(identitySelector)); 
    } 

    public static IEqualityComparer<TSource> By<TSource, TIdentity>(Func<TSource, TIdentity> identitySelector) 
    { 
     return new DelegateComparer<TSource, TIdentity>(identitySelector); 
    } 

    private class DelegateComparer<T, TIdentity> : IEqualityComparer<T> 
    { 
     private readonly Func<T, TIdentity> identitySelector; 

     public DelegateComparer(Func<T, TIdentity> identitySelector) 
     { 
      this.identitySelector = identitySelector; 
     } 

     public bool Equals(T x, T y) 
     { 
      return Equals(identitySelector(x), identitySelector(y)); 
     } 

     public int GetHashCode(T obj) 
     { 
      return identitySelector(obj).GetHashCode(); 
     } 
    } 
} 

nào mang đến cho bạn cú pháp:

source.DistinctBy(a => a.Id); 

Hoặc, nếu bạn cảm thấy nó rõ ràng hơn theo cách này:

source.Distinct(Compare.By(a => a.Id)); 
+2

Sau khi làm như vậy, bạn cũng có thể viết phương thức mở rộng của riêng bạn để có một đại biểu. –

+0

@David, chính xác :-) – driis

+0

Tuyệt vời. Tôi đã bỏ lỡ chức năng này từ khi bắt đầu LINQ. – Konamiman

10

Thật không may là Distinct không đến với tình trạng quá tải như vậy, vì vậy những gì bạn có là một lựa chọn tốt.

Với MoreLinq, bạn có thể sử dụng toán tử DistinctBy.

var distincts = bundle.GetAllThings.DistinctBy(a => a.Id); 

Bạn cũng có thể muốn xem xét việc viết một generic ProjectionEqualityComparer có thể biến các đại biểu thích hợp vào một thực hiện IEqualityComparer<T>, chẳng hạn như một trong những liệt kê here.

2

này link cho biết cách tạo phương thức tiện ích để có thể sử dụng Phân biệt theo cách bạn đã cung cấp. Bạn sẽ cần phải viết hai phương thức mở rộng Distinct và một IEqualityComparer.

Dưới đây là đoạn code, từ trang web:

public static class Extensions 
    { 
     public static IEnumerable<T> Distinct<T>(this IEnumerable<T> source, Func<T, T, bool> comparer) 
     {   
      return source.Distinct(new DelegateComparer<T>(comparer)); 
     } 

     public static IEnumerable<T> Distinct<T>(this IEnumerable<T> source, Func<T, T, bool> comparer, Func<T,int> hashMethod) 
     { 
      return source.Distinct(new DelegateComparer<T>(comparer,hashMethod)); 
     } 
    } 

    public class DelegateComparer<T> : IEqualityComparer<T> 
    { 
     private Func<T, T, bool> _equals; 
     private Func<T,int> _getHashCode; 

     public DelegateComparer(Func<T, T, bool> equals) 
     { 
      this._equals = equals; 
     } 

     public DelegateComparer(Func<T, T, bool> equals, Func<T,int> getHashCode) 
     { 
      this._equals = equals; 
      this._getHashCode = getHashCode; 
     } 

     public bool Equals(T a, T b) 
     { 
      return _equals(a, b); 
     } 

     public int GetHashCode(T a) 
     { 
      if (_getHashCode != null)  
       return _getHashCode(a);  
      else 
       return a.GetHashCode(); 
     } 
    } 
+4

Vấn đề với mã này là nó không tuân theo các quy tắc cho GetHashCode và Equals (xem msdn.microsoft.com/en-us/library/system.object.gethashcode.aspx). Bất cứ lúc nào bạn sử dụng quá tải đầu tiên trên một lớp tùy chỉnh, bạn rất có khả năng nhận được kết quả không chính xác. GetHashCode * phải * trả về cùng một giá trị cho hai đối tượng khi Equals trả về true. –

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