Các hàm bao có nên so sánh như nhau bằng cách sử dụng toán tử == khi chúng bao bọc cùng một đối tượng?


19

Tôi đang viết một trình bao bọc cho các phần tử XML cho phép nhà phát triển dễ dàng phân tích các thuộc tính từ XML. Trình bao bọc không có trạng thái nào khác ngoài đối tượng được bọc.

Tôi đang xem xét việc thực hiện sau đây (đơn giản hóa cho ví dụ này) bao gồm quá tải cho ==toán tử.

class XmlWrapper
{
    protected readonly XElement _element;

    public XmlWrapper(XElement element)
    {
        _element = element;
    }

    public string NameAttribute
    {
        get
        {
            //Get the value of the name attribute
        }
        set
        {
            //Set the value of the name attribute
        }
    }

    public override bool Equals(object other)
    {
        var o = other as XmlWrapper;
        if (o == null) return false;
        return _element.Equals(o._element);
    }

    public override int GetHashCode()
    {
        return _element.GetHashCode();
    }

    static public bool operator == (XmlWrapper lhs, XmlWrapper rhs)
    {
        if (ReferenceEquals(lhs, null) && ReferenceEquals(rhs, null)) return true;
        if (ReferenceEquals(lhs, null) || ReferenceEquals(rhs, null)) return false;

        return lhs._element == rhs._element;
    }

    static public bool operator != (XmlWrapper lhs, XmlWrapper rhs)
    {
        return !(lhs == rhs);
    }
}

Theo tôi hiểu c # thành ngữ, ==toán tử là cho đẳng thức tham chiếu trong khi Equals()phương thức là cho đẳng thức giá trị. Nhưng trong trường hợp này, "giá trị" chỉ là một tham chiếu đến đối tượng được bọc. Vì vậy, tôi không rõ những gì là thông thường hoặc thành ngữ cho c #.

Ví dụ: trong mã này ...

var underlyingElement = new XElement("Foo");
var a = new XmlWrapper(underlyingElement);
var b = new XmlWrapper(underlyingElement);

a.NameAttribute = "Hello";
b.NameAttribute = "World";

if (a == b)
{
    Console.WriteLine("The wrappers a and b are the same.");
}

.... chương trình đầu ra "Các hàm bao a và b có giống nhau không"? Hoặc đó sẽ là kỳ lạ, tức là vi phạm hiệu trưởng ít ngạc nhiên nhất ?


Đối với tất cả các lần tôi vượt qua Equalstôi không bao giờ vượt qua ==(nhưng không bao giờ cách khác). Là lười biếng thành ngữ? Nếu tôi có hành vi khác nhau mà không có diễn viên rõ ràng vi phạm ít kinh ngạc nhất.
radarbob

Câu trả lời cho điều này phụ thuộc vào việc NameAttribution làm gì - sửa đổi thành phần cơ bản? Là một phần bổ sung của dữ liệu? Ý nghĩa của mã ví dụ (và liệu nó có nên được coi là bằng nhau hay không) thay đổi tùy theo điều đó, vì vậy tôi nghĩ bạn cần điền nó vào.
Erroratz

@Errorsatz Tôi xin lỗi, nhưng tôi muốn giữ cho ví dụ ngắn gọn và tôi cho rằng rõ ràng là nó sẽ sửa đổi phần tử được bao bọc (cụ thể bằng cách sửa đổi thuộc tính XML có tên là "name.") rằng trình bao bọc cho phép truy cập đọc / ghi vào phần tử được bao bọc nhưng không chứa trạng thái của chính nó.
John Wu

4
Chà, trong trường hợp này chúng quan trọng - điều đó có nghĩa là việc gán "Xin chào" và "Thế giới" là sai lệch, bởi vì cái sau sẽ ghi đè lên cái trước. Điều đó có nghĩa là tôi đồng ý với câu trả lời của Martin rằng hai người có thể được coi là ngang nhau. Nếu NameAttribution thực sự khác nhau giữa chúng, tôi sẽ không coi chúng bằng nhau.
Erroratz

2
"Theo tôi hiểu c # thành ngữ, toán tử == là cho đẳng thức tham chiếu trong khi phương thức Equals () là cho đẳng thức giá trị." Có phải mặc dù? Hầu hết các lần tôi đã thấy == quá tải là vì sự bình đẳng giá trị. Ví dụ quan trọng nhất là System.String.
Arturo Torres Sánchez

Câu trả lời:


17

Vì tham chiếu đến bao bọc XElementlà không thay đổi, không có sự khác biệt có thể quan sát được bên ngoài giữa hai trường hợp XmlWrapperbao bọc cùng một phần tử, do đó, có nghĩa là quá tải ==để phản ánh thực tế này.

Mã khách hàng hầu như luôn quan tâm đến sự bằng nhau logic (theo mặc định, được triển khai bằng cách sử dụng đẳng thức tham chiếu cho các loại tham chiếu). Thực tế là có hai trường hợp trên heap là một chi tiết triển khai mà khách hàng không nên quan tâm (và những trường hợp sẽ sử dụng Object.ReferenceEqualstrực tiếp).


9

Nếu bạn nghĩ nó có ý nghĩa nhất

Câu hỏi và câu trả lời là một vấn đề của sự mong đợi của nhà phát triển , đây không phải là một yêu cầu kỹ thuật.

NẾU bạn coi một trình bao bọc không có danh tính và được xác định hoàn toàn bởi nội dung của nó, thì câu trả lời cho câu hỏi của bạn là có.

Nhưng đây là một vấn đề định kỳ. Hai trình bao bọc có nên thể hiện sự bình đẳng khi chúng bao bọc các đối tượng khác nhau nhưng với cả hai đối tượng có cùng nội dung?

Câu trả lời lặp lại. NẾU các đối tượng nội dung không có danh tính cá nhân và thay vào đó được xác định hoàn toàn bởi nội dung của chúng, thì các đối tượng nội dung là các trình bao bọc hiệu quả sẽ thể hiện sự bình đẳng. Nếu sau đó bạn bọc các đối tượng nội dung trong một trình bao bọc khác, thì trình bao bọc (bổ sung) đó cũng sẽ thể hiện sự bình đẳng.

Đó là tất cả các con rùa xuống .


Mẹo chung

Bất cứ khi nào bạn đi chệch khỏi hành vi mặc định, nó cần được ghi lại rõ ràng. Là một nhà phát triển, tôi hy vọng rằng hai loại tham chiếu sẽ không thể hiện sự bình đẳng ngay cả khi nội dung của chúng bằng nhau. Nếu bạn thay đổi hành vi đó, tôi sẽ đề nghị bạn ghi lại rõ ràng để tất cả các nhà phát triển nhận thức được hành vi không điển hình này.


Theo tôi hiểu c # thành ngữ, ==toán tử là cho đẳng thức tham chiếu trong khi Equals()phương thức là cho đẳng thức giá trị.

Đó là hành vi mặc định của nó, nhưng đây không phải là quy tắc bất động. Đó là một vấn đề của quy ước, nhưng các quy ước có thể được thay đổi khi hợp lý .

stringlà một ví dụ tuyệt vời ở đây, ==cũng là một kiểm tra bình đẳng giá trị (ngay cả khi không có chuỗi thực tập!). Tại sao? Nói một cách đơn giản: bởi vì có các chuỗi hoạt động giống như các đối tượng giá trị cảm thấy trực quan hơn đối với hầu hết các nhà phát triển.

Nếu cơ sở mã của bạn (hoặc cuộc sống của các nhà phát triển của bạn) có thể được đơn giản hóa đáng kể bằng cách các trình bao bọc của bạn thể hiện sự bình đẳng giá trị trên bảng, hãy tìm kiếm nó (nhưng ghi lại nó ).

Nếu bạn không bao giờ yêu cầu kiểm tra bình đẳng tham chiếu (hoặc chúng bị vô hiệu hóa bởi lĩnh vực kinh doanh của bạn), thì không có lý do gì để giữ kiểm tra bình đẳng tham chiếu. Sau đó, tốt hơn là thay thế nó bằng kiểm tra bình đẳng giá trị để tránh lỗi nhà phát triển .
Tuy nhiên, hãy nhận ra rằng bạn nên kiểm tra sự bình đẳng tham chiếu sau khi xuống dòng, thực hiện lại nó có thể mất một nỗ lực đáng chú ý.


Tôi tò mò tại sao bạn mong đợi rằng các loại tham chiếu sẽ không xác định sự bình đẳng nội dung. Hầu hết các loại không định nghĩa sự bình đẳng đơn giản vì nó không cần thiết cho miền của họ, không phải vì họ muốn bình đẳng tham chiếu.
casifer

3
@casTHER: Tôi nghĩ rằng bạn đang diễn giải một định nghĩa khác về "kỳ vọng" (nghĩa là yêu cầu so với giả định). Không có tài liệu, tôi mong đợi (tức là giả định) ==kiểm tra sự bình đẳng tham chiếu vì đây là hành vi mặc định. Tuy nhiên, nếu ==thực sự kiểm tra sự bình đẳng giá trị, tôi mong đợi (tức là yêu cầu) rằng điều này được ghi lại rõ ràng. I'm curious why you expect that reference types won't define content equality.Họ không định nghĩa nó theo mặc định , nhưng điều đó không có nghĩa là không thể thực hiện được. Tôi chưa bao giờ nói rằng nó không thể (hoặc không nên) được thực hiện, tôi chỉ đơn giản là không mong đợi (tức là giả định) nó theo mặc định.
Flater

Tôi hiểu ý của bạn, cảm ơn vì đã làm rõ.
casablanca

2

Về cơ bản, bạn đang so sánh các chuỗi vì vậy tôi sẽ ngạc nhiên nếu hai hàm bao chứa cùng một nội dung XML sẽ không được coi là bằng nhau, được kiểm tra bằng Equals hoặc ==.

Quy tắc thành ngữ có thể có ý nghĩa đối với các đối tượng loại tham chiếu nói chung nhưng các chuỗi đặc biệt theo nghĩa thành ngữ, bạn phải coi và coi chúng là các giá trị mặc dù về mặt kỹ thuật chúng là các loại tham chiếu.

Postfix Wrapper của bạn thêm nhầm lẫn mặc dù. Về cơ bản nó nói "không phải là một phần tử XML". Vì vậy, tôi nên coi nó như một loại tài liệu tham khảo? Về mặt ngữ nghĩa, điều này sẽ không có ý nghĩa. Tôi sẽ ít bối rối hơn nếu lớp được đặt tên là XmlContent. Điều này sẽ báo hiệu chúng tôi quan tâm đến nội dung, không phải chi tiết triển khai kỹ thuật.


Nơi nào bạn sẽ đặt giới hạn giữa "một đối tượng được xây dựng từ một chuỗi" và "về cơ bản là một chuỗi"? Có vẻ như một định nghĩa khá mơ hồ từ phối cảnh API bên ngoài. Người dùng API thực sự có phải đoán nội bộ để đoán hành vi không?
Arthur Havlicek

@Arthur Ngữ nghĩa của lớp / đối tượng sẽ cung cấp manh mối. Và đôi khi nó không phải là tất cả rõ ràng, do đó câu hỏi này. Đối với các loại giá trị thực, nó sẽ rõ ràng với bất cứ ai. Đối với chuỗi thực cũng có. Loại cuối cùng sẽ cho biết liệu một so sánh sẽ liên quan đến nội dung hoặc danh tính đối tượng.
Martin Maat
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.