Haruskah pembungkus membandingkan sama dengan menggunakan operator == ketika mereka membungkus objek yang sama?

19

Saya sedang menulis pembungkus untuk elemen XML yang memungkinkan pengembang untuk dengan mudah mengurai atribut dari XML. Pembungkus tidak memiliki status selain objek yang dibungkus.

Saya sedang mempertimbangkan implementasi berikut (disederhanakan untuk contoh ini) yang mencakup kelebihan bagi ==operator.

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

Seperti yang saya mengerti c # idiomatik, ==operator adalah untuk referensi kesetaraan sedangkan Equals()metode untuk kesetaraan nilai. Tetapi dalam kasus ini, "nilai" hanyalah referensi ke objek yang dibungkus. Jadi saya tidak jelas apa yang konvensional atau idiomatik untuk c #.

Misalnya, dalam kode ini ...

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.");
}

.... haruskah keluaran program "Pembungkus a dan b sama"? Atau apakah itu aneh, yaitu melanggar prinsip paling tidak heran ?

John Wu
sumber
Untuk semua waktu saya mengalahkan Equalssaya tidak pernah mengalahkan ==(tapi tidak pernah sebaliknya). Apakah malas idiomatis? Jika saya mendapatkan perilaku yang berbeda tanpa pemeran eksplisit yang melanggar paling tidak heran.
radarbob
Jawabannya tergantung pada apa yang dilakukan NameAttribute - memodifikasi elemen yang mendasarinya? Apakah sepotong data tambahan? Arti kode contoh (dan apakah harus dianggap sama) berubah tergantung pada itu, jadi saya pikir Anda perlu mengisinya.
Errorsatz
@Errorsatz saya minta maaf, tapi saya ingin menjaga contoh ringkas, dan saya berasumsi jelas bahwa itu akan memodifikasi elemen yang dibungkus (khusus dengan memodifikasi atribut XML bernama "nama.") Tapi spesifikasinya tidak masalah - intinya adalah bahwa pembungkus memungkinkan akses baca / tulis ke elemen yang dibungkus tetapi tidak mengandung keadaannya sendiri.
John Wu
4
Nah, dalam kasus ini mereka penting - itu berarti bahwa tugas "Hello" dan "World" menyesatkan, karena yang terakhir akan menimpa yang pertama. Yang berarti saya setuju dengan jawaban Martin bahwa keduanya dapat dianggap sama. Jika NameAttribute benar-benar berbeda di antara mereka, saya tidak akan menganggap mereka sama.
Errorsatz
2
"Seperti yang saya mengerti c # idiomatik, operator == adalah untuk persamaan referensi sedangkan metode Equals () adalah untuk persamaan nilai." Apakah itu? Sebagian besar waktu yang saya lihat == kelebihan beban adalah untuk kesetaraan nilai. Contoh paling penting adalah System.String.
Arturo Torres Sánchez

Jawaban:

17

Karena referensi ke bungkus XElementtidak dapat diubah, tidak ada perbedaan yang dapat diamati secara eksternal antara dua contoh XmlWrapperbungkus itu dengan elemen yang sama, jadi masuk akal untuk membebani ==untuk mencerminkan fakta ini.

Kode klien hampir selalu peduli tentang kesetaraan logis (yang, secara default, diimplementasikan menggunakan persamaan referensi untuk jenis referensi). Fakta bahwa ada dua contoh pada heap adalah detail implementasi yang tidak perlu dipedulikan oleh klien (dan yang akan digunakan Object.ReferenceEqualssecara langsung).

casablanca
sumber
9

Jika Anda pikir itu yang paling masuk akal

Pertanyaan dan jawaban adalah masalah harapan pengembang , ini bukan persyaratan teknis.

JIKA Anda menganggap pembungkus untuk tidak memiliki identitas dan membuatnya murni berdasarkan isinya, maka jawaban untuk pertanyaan Anda adalah ya.

Tapi ini masalah berulang. Haruskah dua pembungkus menunjukkan kesetaraan ketika mereka membungkus benda yang berbeda tetapi dengan kedua benda memiliki konten yang sama persis?

Jawabannya berulang. JIKA objek konten tidak memiliki identitas pribadi dan sebagai gantinya murni ditentukan oleh konten mereka, maka objek konten secara efektif adalah pembungkus yang akan menunjukkan kesetaraan. Jika Anda kemudian membungkus objek konten dalam pembungkus lain, pembungkus (tambahan) itu juga harus menunjukkan kesetaraan.

Ini kura - kura sepanjang jalan .


Tip umum

Setiap kali Anda menyimpang dari perilaku default, itu harus didokumentasikan secara eksplisit. Sebagai pengembang, saya berharap bahwa dua tipe referensi tidak akan menunjukkan kesetaraan bahkan jika isinya sama. Jika Anda mengubah perilaku itu, saya sarankan Anda mendokumentasikannya dengan jelas sehingga semua pengembang mengetahui perilaku tidak lazim ini.


Seperti yang saya mengerti c # idiomatik, ==operator adalah untuk referensi kesetaraan sedangkan Equals()metode untuk kesetaraan nilai.

Itu adalah perilaku default-nya, tetapi ini bukan aturan yang tidak bisa digerakkan. Ini masalah konvensi, tetapi konvensi dapat diubah jika dapat dibenarkan .

stringadalah contoh yang bagus di sini, seperti ==juga pemeriksaan kesetaraan nilai (bahkan ketika tidak ada string magang!). Mengapa? Sederhananya: karena memiliki string berperilaku seperti objek nilai terasa lebih intuitif untuk sebagian besar pengembang.

Jika basis kode Anda (atau kehidupan pengembang Anda) dapat disederhanakan dengan meminta pembungkus Anda menunjukkan kesetaraan nilai di seluruh papan, lakukan (tapi dokumentasikan ).

Jika Anda tidak pernah memerlukan pemeriksaan kesetaraan referensi (atau mereka dianggap tidak berguna oleh domain bisnis Anda), maka tidak ada gunanya menyimpan pemeriksaan kesetaraan referensi di sekitar. Lebih baik menggantinya dengan pemeriksaan kesetaraan nilai untuk mencegah kesalahan pengembang .
Namun, sadarilah bahwa jika Anda memerlukan referensi pemeriksaan kesetaraan nanti di kemudian hari, mengimplementasikannya mungkin membutuhkan upaya yang penting.

Flater
sumber
Saya ingin tahu mengapa Anda berharap bahwa jenis referensi tidak akan mendefinisikan kesetaraan konten. Sebagian besar jenis tidak mendefinisikan kesetaraan hanya karena itu tidak penting untuk domain mereka, bukan karena mereka ingin referensi kesetaraan.
casablanca
3
@casablanca: Saya pikir Anda menafsirkan definisi yang berbeda dari "harapan" (yaitu persyaratan versus asumsi). Tanpa dokumentasi, saya berharap (yaitu berasumsi) yang ==memeriksa kesetaraan referensi karena ini adalah perilaku default. Namun, jika ==benar-benar memeriksa kesetaraan nilai, saya berharap (yaitu mengharuskan) bahwa ini didokumentasikan secara eksplisit. I'm curious why you expect that reference types won't define content equality.Mereka tidak mendefinisikannya secara default , tetapi itu tidak berarti itu tidak dapat dilakukan. Saya tidak pernah mengatakan itu tidak dapat (atau tidak seharusnya) dilakukan, saya hanya tidak berharap (yaitu menganggap) secara default.
Flater
Saya mengerti maksud Anda, terima kasih telah menjelaskan.
casablanca
2

Anda pada dasarnya membandingkan string jadi saya akan heran jika dua pembungkus yang berisi konten XML yang sama tidak akan dianggap sama, apakah itu diperiksa menggunakan Persamaan atau ==.

Aturan idiomatik mungkin masuk akal untuk objek jenis referensi secara umum tetapi string khusus dalam arti idiomatik, Anda seharusnya memperlakukan dan menganggapnya sebagai nilai meskipun secara teknis mereka adalah tipe referensi.

Postfix Wrapper Anda menambah kebingungan. Itu pada dasarnya mengatakan "bukan elemen XML". Jadi, haruskah saya memperlakukannya sebagai tipe referensi? Semantik ini tidak masuk akal. Saya akan kurang bingung jika kelas itu bernama XmlContent. Ini menandakan kami peduli dengan konten, bukan detail implementasi teknis.

Martin Maat
sumber
Di mana Anda meletakkan batas antara "objek yang dibangun dari string" dan "pada dasarnya string"? Tampaknya definisi yang agak ambigu dari perspektif API luar. Apakah pengguna API benar-benar harus menebak internal untuk menebak perilaku?
Arthur Havlicek
@Arthur Semantik dari kelas / objek harus memberikan petunjuk. Dan kadang-kadang tidak begitu jelas, maka pertanyaan ini. Untuk tipe nilai nyata, hal ini akan jelas bagi siapa saja. Untuk string nyata juga. Jenis tersebut pada akhirnya harus mengetahui apakah perbandingan harus melibatkan konten atau identitas objek.
Martin Maat