2013-03-19 14 views
6

Tôi đã đọc các câu hỏi khác nhau tương tự như của tôi nhưng không ai trong số họ có vẻ giải quyết vấn đề của tôi.Triển khai IEquatable <T> khi T có thể là IEnumerable <T>

Tôi đã một kiểu như thế này:

class MyObject<T> : IEquatable<MyObject<T>> { // no generic constraints 
    private readonly string otherProp; 
    private readonly T value; 

    public MyObject(string otherProp, T value) 
    { 
    this.otherProp = otherProp; 
    this.value = value; 
    } 

    public string OtherProp { get { return this.otherProp; } } 

    public T Value { get { return this.value; } } 

    // ... 

    public bool Equals(MyObject<T> other) 
    { 
    if (other == null) 
    { 
     return false; 
    } 

    return this.OtherProp.Equals(other.OtherProp) && this.Value.Equals(other.Value); 
    } 
} 

Khi T là một vô hướng như MyObject<int> bình đẳng hoạt động chính xác, nhưng khi tôi định nghĩa một cái gì đó giống như MyObject<IEnumerable<int>> bình đẳng thất bại.

Lý do là khi T là IEnumerable<T> Tôi nên gọi this.Value.SequenceEqual(other.Value).

Xử lý sự khác biệt này bloats Equals(MyObject<T>) với quá LOC loại kiểm tra và phản ánh (đối với tôi, dẫn đến vi phạm SOLID/SRP).

Tôi không thể tìm thấy trường hợp cụ thể này trong nguyên tắc MSDN, vì vậy nếu có ai đó đã gặp sự cố này; nó sẽ là tuyệt vời nếu kiến ​​thức này có thể được chia sẻ.

Edit: Alternative

Để K.I.S.S., tôi đang tự hỏi để làm một cái gì đó tương tự:

class MyObject<T> : IEquatable<MyObject<T>> { 
    private readonly IEnumerable<T> value; 

    // remainder omitted 
} 

Bằng cách này, thực hiện Equal sẽ có rất nhiều đơn giản. Và khi tôi chỉ cần một giá trị tôi đã có một bộ sưu tập gồm 1 mục. Rõ ràng T sẽ không được liệt kê (nhưng cấu trúc dữ liệu là riêng tư nên không có vấn đề gì).

+0

Nếu có thể trong trường hợp của bạn, bạn cũng có thể thêm ràng buộc chung 'trong đó T: IEquatable '. – Guillaume

+0

@Guillaume, tôi đã đánh giá và lý do về nó ... – jay

+0

Để tham khảo, tôi muốn thêm những gì đi trong câu trả lời theo __Edit: Alternative__ gần với giải pháp thực sự của tôi. Tôi đang gặp phải rằng không có lý do hợp lệ để không sử dụng một 'IEnumerable ' cũng khi trong hầu hết các trường hợp, tập hợp 'T' sẽ chỉ giữ một phần tử hoặc thay vì trống. Đây không phải là trường hợp, bộ sưu tập thường giữ từ một đến vài yếu tố; vì vậy tôi muốn nói rằng 'IEnumerable 'nên được _hard coded_ trong định nghĩa' MyObject 'thay vì tùy chọn được truyền như một toàn bộ thông qua kiểu tham số (tôi đang nói về việc tránh' MyObject > 'và đi cho thay thế). – jay

Trả lời

3

Bạn có thể có của bạn MyObject<T> lấy một comparer bình đẳng cho loại hình của bạn T và sử dụng rằng:

class MyObject<T> : IEquatable<MyObject<T>> 
{ 
    private readonly IEqualityComparer<T> comparer; 
    public MyObject(string otherProp, T value, IEqualityComparer<T> comparer) 
    { 
     this.comparer = comparer; 
    } 
    public MyObject(string otherProp, T value) 
     : this(otherProp, value, EqualityComparer<T>.Default) 
    { 
    } 

    public bool Equals(MyObject<T> other) 
    { 
     return OtherProp.Equals(other.OtherProp) && comparer.Equals(this.Value, other.Value); 
    } 
} 

Sau đó cho IEnumerable<T> bạn có thể sử dụng một Comparer mà so sánh trình tự thay vì tham chiếu.Bạn có thể muốn sử dụng các phương pháp nhà máy để tạo các đối tượng của mình để đảm bảo cùng một loại so sánh được sử dụng cho cùng một số T để đảm bảo sự bình đẳng vẫn là đối xứng.

+0

+1, mã được viết tốt. Chỉ cần để chỉ ra nó, điều này sẽ làm việc tự động cho các loại như 'int' (kể từ khi nó được đề cập trong câu hỏi)? – Default

+0

@Lee +1 cho nhà máy được cung cấp cùng với 'constructor injection'; nhưng như đã nêu trong một bình luận khác và trong bản chỉnh sửa mới nhất của tôi, tôi muốn tránh viết mã cơ sở hạ tầng. – jay

+0

@Lee, được đánh dấu là đã trả lời vì như đã nêu trong bình luận trước tôi thích sử dụng C.I. để vượt qua sự phụ thuộc. – jay

1

Không cần phản ánh. Một lựa chọn là để kiểm tra xem giá trị là IEnumerable hay không trong phương pháp Equals của bạn:

IEnumerable e = other.Value as IEnumerable; 

if(e != null){ 
// use SequenceEqual 
}else{ 
// use Equals 
} 
+0

Đã thử giải pháp này, @alex? Tôi nghĩ là không đơn giản như được trình bày ở đây. 'SequenceEqual' cần một tham số kiểu để bạn có thể đưa vào' IEnumerable '. Để nói nhiều hơn, cá thể hiện tại có thể là một vô hướng hoặc một số đếm của một T. khác nhau. Nhiều hơn nữa kiểm tra một tham gia và tôi không nghĩ rằng bạn có thể bỏ qua sự phản chiếu chút nào. Có nhiều liên quan hơn: 'string' nên được coi là một vô hướng nhưng cũng' chuỗi' thực hiện IEnumerable . – jay

+0

Bạn có thể viết một phương pháp so sánh bình đẳng "phổ quát" có hai đối tượng. Nếu cả hai đều có thể đếm được, thì nó sẽ lặp lại chúng. Để so sánh các mục của IEnumerable nó gọi chính nó. Vì vậy, mảng mảng sẽ được so sánh chính xác. Bạn cũng có thể thêm một số ngoại lệ vào chuỗi, làm tối ưu hóa hiệu suất. – alex

+0

xin lỗi nhưng tôi muốn giữ mọi thứ đơn giản. Tôi sẽ không viết và thử nghiệm một đoạn mã cơ sở hạ tầng chung chung. Như bạn có thể thấy từ bản chỉnh sửa mới nhất của tôi cho câu hỏi, tôi đang tìm kiếm một K.I.S.S. dung dịch. – jay

4

Trong trường hợp khi TIEnumerable<T>, mã của bạn so sánh hai tài liệu tham khảo của IEnumerable<T> và, tất nhiên, những tài liệu tham khảo có thể không bằng nhau. Trên thực tế, bạn sẽ nhận được hành vi này, khi T sẽ là bất kỳ loại tham chiếu nào mà không có phương thức Equals bị ghi đè.

Nếu bạn không muốn so sánh các tham chiếu ở đây, bạn nên viết mã, sẽ so sánh các chuỗi này theo nội dung của chúng. Tuy nhiên, bạn nên lưu ý, chuỗi đó có thể là vô tận.

+0

Tôi sắp chỉnh sửa tiêu đề ... Nhưng bây giờ tôi thấy tôi đã gắn thẻ chính xác nó với 'cấu trúc bình đẳng'. – jay

1

đây là trường hợp bạn cần trợ giúp của lớp trợ giúp, loại MyObject của bạn có thể sử dụng EqualityChecker riêng lẻ.

Tôi muốn nói triển khai mẫu chiến lược với phương pháp nhà máy tĩnh sẽ đơn giản hóa thiết kế của bạn.

một cái gì đó như dưới đây

class MyObject<T> : IEquatable<MyObject<T>> 
{ // no generic constraints 
    private readonly string otherProp; 
    private readonly T value; 

    public MyObject(string otherProp, T value) 
    { 
     this.otherProp = otherProp; 
     this.value = value; 
    } 

    public string OtherProp { get { return this.otherProp; } } 

    public T Value { get { return this.value; } } 

    // ... 

    public bool Equals(MyObject<T> other) 
    { 
     if (other == null) 
     { 
      return false; 
     } 

    var cheker = EqualityChekerCreator<T>.CreateEqualityChecker(other.Value); 

    if (cheker != null) 
     return cheker.CheckEquality(this.Value, other.value); 

     return this.OtherProp.Equals(other.OtherProp) && this.Value.Equals(other.Value); 
    } 
} 

public static class EqualityChekerCreator<T> 
{ 
    private static IEqualityCheker<T> checker; 

    public static IEqualityCheker<T> CreateEqualityChecker(T type) 
    { 
     var currenttype = type as IEnumerable<T>; 

     if(currenttype!=null) 
      checker = new SequenceEqualityChecker<T>(); 

     return checker; 
    } 
} 

public interface IEqualityCheker<in T> 
{ 
    bool CheckEquality(T t1, T t2); 
} 

public class SequenceEqualityChecker <T> : IEqualityCheker<T> 
{ 
    #region Implementation of IEqualityCheker<in T> 

    public bool CheckEquality(T t1, T t2) 
    { 
     // implement method here; 
    } 

    #endregion 
}