Estou escrevendo um wrapper para elementos XML que permite que um desenvolvedor analise facilmente atributos do XML. O wrapper não possui outro estado além do objeto que está sendo quebrado.
Estou considerando a seguinte implementação (simplificada para este exemplo), que inclui uma sobrecarga para o ==
operador.
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);
}
}
Pelo que entendi c # idiomático, o ==
operador é para igualdade de referência, enquanto o Equals()
método é para igualdade de valor. Mas, neste caso, o "valor" é apenas uma referência ao objeto que está sendo quebrado. Portanto, não estou claro o que é convencional ou idiomático para c #.
Por exemplo, neste código ...
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.");
}
.... o programa deve exibir "Os wrappers aeb são iguais"? Ou isso seria estranho, ou seja, violaria o princípio de menor espanto ?
Equals
eu nunca substituí==
(mas nunca o contrário). O idiota preguiçoso? Se eu tiver um comportamento diferente sem um elenco explícito que viole menos espanto.Respostas:
Como a referência ao empacotado
XElement
é imutável, não há diferença observável externamente entre duas instânciasXmlWrapper
desse empacotamento no mesmo elemento; portanto, faz sentido sobrecarregar==
para refletir esse fato.O código do cliente quase sempre se preocupa com a igualdade lógica (que, por padrão, é implementada usando a igualdade de referência para tipos de referência). O fato de haver duas instâncias no heap é um detalhe de implementação com o qual os clientes não devem se preocupar (e aqueles que o fizerem usarão
Object.ReferenceEquals
diretamente).fonte
Se você acha que faz mais sentido
A pergunta e a resposta são uma questão de expectativa do desenvolvedor , esse não é um requisito técnico.
Se você considera que um invólucro não possui uma identidade e a define apenas pelo conteúdo, a resposta para sua pergunta é sim.
Mas este é um problema recorrente. Dois invólucros devem exibir igualdade quando envolvem objetos diferentes, mas com os dois objetos com exatamente o mesmo conteúdo?
A resposta se repete. Se os objetos de conteúdo não tiverem identidade pessoal e, em vez disso, forem puramente definidos por seu conteúdo, eles serão efetivamente invólucros que exibirão igualdade. Se você agrupar os objetos de conteúdo em outro invólucro, esse invólucro (adicional) também deverá exibir igualdade.
São tartarugas até o fim .
Dica geral
Sempre que você se desvia do comportamento padrão, ele deve ser explicitamente documentado. Como desenvolvedor, espero que dois tipos de referência não apresentem igualdade, mesmo que seu conteúdo seja igual. Se você mudar esse comportamento, sugiro que você o documente claramente para que todos os desenvolvedores estejam cientes desse comportamento atípico.
Esse é o seu comportamento padrão, mas essa não é uma regra imóvel. É uma questão de convenção, mas as convenções podem ser alteradas quando justificadas .
string
é um ótimo exemplo aqui, como==
também é uma verificação de igualdade de valor (mesmo quando não há cadeia de caracteres interna!). Por quê? Simplificando: porque ter seqüências de caracteres se comporta como objetos de valor parece mais intuitivo para a maioria dos desenvolvedores.Se a sua base de código (ou a vida de seus desenvolvedores) puder ser notavelmente simplificada, fazendo com que seus wrappers exibam igualdade de valor em todos os aspectos, faça isso (mas documente ).
Se você nunca exige verificações de igualdade de referência (ou elas são inúteis pelo domínio de negócios), não faz sentido manter uma verificação de igualdade de referência. É melhor substituí-lo por uma verificação de igualdade de valor para evitar erros do desenvolvedor .
No entanto, saiba que, se precisar de verificações de igualdade de referência posteriormente, reimplementá-lo pode exigir um esforço notável.
fonte
==
verifique a igualdade de referência, pois esse é o comportamento padrão. No entanto, se==
realmente verificar a igualdade de valor, espero (ou seja, exigir) que isso seja documentado explicitamente.I'm curious why you expect that reference types won't define content equality.
Eles não o definem por padrão , mas isso não significa que não possa ser feito. Eu nunca disse que não pode (ou não deveria) ser feito, simplesmente não espero (ou seja, assumo) isso por padrão.Você está basicamente comparando cadeias de caracteres, então eu ficaria surpreso se dois wrappers contendo o mesmo conteúdo XML não fossem considerados iguais, seja verificado usando Equals ou ==.
A regra idiomática pode fazer sentido para objetos do tipo de referência em geral, mas as strings são especiais em um sentido idiomático; você deve tratá-las e considerá-las como valores, embora tecnicamente sejam tipos de referência.
O seu postfix do Wrapper acrescenta confusão. Basicamente, diz "não é um elemento XML". Então, devo tratá-lo como um tipo de referência, afinal? Semanticamente, isso não faria sentido. Eu ficaria menos confuso se a classe fosse nomeada XmlContent. Isso indicaria que nos preocupamos com o conteúdo, não com os detalhes técnicos da implementação.
fonte