Piszę opakowanie dla elementów XML, które pozwala programistom łatwo analizować atrybuty z XML. Opakowanie nie ma innego stanu niż zawijany obiekt.
Rozważam następującą implementację (uproszczoną w tym przykładzie), która obejmuje przeciążenie ==
operatora.
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);
}
}
Jak rozumiem idiomatyczne c #, ==
operator służy do równości odniesienia, podczas gdy Equals()
metoda służy do równości wartości. Ale w tym przypadku „wartość” jest tylko odniesieniem do opakowanego obiektu. Więc nie jestem jasne, co jest konwencjonalne lub idiomatyczne dla c #.
Na przykład w tym kodzie ...
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.");
}
.... czy program powinien wypisać „Opakowania aib są takie same”? Czy byłoby to dziwne, tj. Naruszałoby zasadę najmniejszego zdziwienia ?
Equals
nigdy nie przesadzam==
(ale nigdy na odwrót). Czy leniwy idiomatyczny? Jeśli dostanę inne zachowanie bez wyraźnej obsady, która narusza najmniejsze zdziwienie.Odpowiedzi:
Ponieważ odniesienie do owinięcia
XElement
jest niezmienne, nie ma zewnętrznej obserwowalnej różnicy między dwoma wystąpieniamiXmlWrapper
tego owinięcia tego samego elementu, więc sensowne jest przeciążenie,==
aby odzwierciedlić ten fakt.Kod klienta prawie zawsze dba o logiczną równość (która domyślnie jest implementowana przy użyciu równości referencyjnej dla typów referencyjnych). Fakt, że na stercie znajdują się dwa wystąpienia, jest szczegółem implementacji, o który klienci nie powinni się przejmować (a ci, którzy to robią, będą używać
Object.ReferenceEquals
bezpośrednio).źródło
Jeśli uważasz, że ma to jak najbardziej sens
Pytanie i odpowiedź jest kwestią oczekiwań programistów , nie jest to wymóg techniczny.
JEŻELI uważasz, że opakowanie nie ma tożsamości i jest zdefiniowane wyłącznie na podstawie jego zawartości, odpowiedź na twoje pytanie brzmi „tak”.
Ale to jest powtarzający się problem. Czy dwa owijki powinny wykazywać równość, gdy owijają różne obiekty, ale oba obiekty mają dokładnie taką samą zawartość?
Odpowiedź się powtarza. JEŻELI obiekty treści nie mają żadnej tożsamości osobistej i zamiast tego są wyłącznie zdefiniowane przez ich treść, wówczas obiekty treści są faktycznie opakowaniami, które będą wykazywać równość. Jeśli następnie zawiniesz obiekty treści w inne opakowanie, to (dodatkowe) opakowanie powinno również wykazywać równość.
To żółwie aż do końca .
Ogólna wskazówka
Ilekroć odstępujesz od domyślnego zachowania, powinno to być wyraźnie udokumentowane. Jako programista oczekuję, że dwa typy referencyjne nie będą wykazywać równości, nawet jeśli ich zawartość będzie równa. Jeśli zmienisz to zachowanie, sugeruję wyraźne udokumentowanie go, aby wszyscy programiści byli świadomi tego nietypowego zachowania.
Takie jest jego domyślne zachowanie, ale nie jest to reguła niezmienna. Jest to kwestia konwencji, ale konwencje można zmienić tam, gdzie jest to uzasadnione .
string
jest tutaj świetnym przykładem, podobnie jak==
kontrola równości wartości (nawet gdy nie ma internowania ciągów!). Dlaczego? Mówiąc prosto: ponieważ ciągi zachowują się jak obiekty wartości, dla większości programistów są bardziej intuicyjne.Jeśli twoją bazę kodu (lub życie programistów) można znacznie uprościć, sprawiając, że twoje opakowania wykazują równość wartości we wszystkich obszarach, idź do niej (ale udokumentuj ją ).
Jeśli nigdy nie potrzebujesz sprawdzania równości referencji (lub są one bezużyteczne w Twojej domenie biznesowej), nie ma sensu utrzymywać kontroli równości referencji. Lepiej jest zastąpić go sprawdzeniem równości wartości, aby uniknąć błędu programisty .
Jednak zdaj sobie sprawę, że jeśli potrzebujesz późniejszej kontroli równości odniesienia, jej ponowne wdrożenie może wymagać znacznego wysiłku.
źródło
==
sprawdza równość referencji, ponieważ jest to zachowanie domyślne. Jeśli jednak==
faktycznie sprawdza równość wartości, oczekuję (tj. Wymagam), że jest to wyraźnie udokumentowane. DomyślnieI'm curious why you expect that reference types won't define content equality.
tego nie definiują , ale to nie znaczy, że nie da się tego zrobić. Nigdy nie powiedziałem, że nie można (lub nie powinien) być zrobiony, po prostu nie oczekuję (tj. Zakładam) domyślnie.Zasadniczo porównujesz ciągi, więc byłbym zdziwiony, gdyby dwa opakowania zawierające tę samą treść XML nie były uważane za równe, niezależnie od tego, czy sprawdza się je za pomocą Równości czy ==.
Reguła idiomatyczna może mieć sens dla obiektów typu referencyjnego w ogóle, ale ciągi znaków są specjalne w sensie idiomatycznym, należy traktować je i traktować jako wartości, chociaż technicznie są to typy referencyjne.
Twój postfiks Wrapper dodaje zamieszania. Mówi w zasadzie „nie jest elementem XML”. Czy powinienem jednak traktować to jako typ referencyjny? Semantycznie nie miałoby to sensu. Byłbym mniej zdezorientowany, gdyby klasa miała nazwę XmlContent. Oznaczałoby to, że zależy nam na treści, a nie technicznych szczegółach implementacji.
źródło