J'écris un wrapper pour les éléments XML qui permet à un développeur d'analyser facilement les attributs du XML. L'encapsuleur n'a aucun état autre que l'objet encapsulé.
Je considère l'implémentation suivante (simplifiée pour cet exemple) qui inclut une surcharge pour l'opérateur ==
.
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);
}
}
Si je comprends bien c # idiomatique, l'opérateur ==
Est pour l'égalité de référence tandis que la méthode Equals()
est pour l'égalité des valeurs. Mais dans ce cas, la "valeur" n'est qu'une référence à l'objet encapsulé. Je ne sais donc pas ce qui est conventionnel ou idiomatique pour c #.
Par exemple, dans ce code ...
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.");
}
.... le programme devrait-il afficher "Les enveloppes a et b sont les mêmes"? Ou serait-ce étrange, c'est-à-dire violer le principe du moindre étonnement ?
Étant donné que la référence au XElement
encapsulé est immuable, il n'y a pas de différence externe observable entre deux instances de XmlWrapper
qui encapsulent le même élément, il est donc logique de surcharger ==
pour refléter ce fait.
Le code client se soucie presque toujours de l'égalité logique (qui, par défaut, est implémentée en utilisant l'égalité de référence pour les types de référence). Le fait qu'il y ait deux instances sur le tas est un détail d'implémentation dont les clients ne devraient pas se soucier (et ceux qui le feront utiliser Object.ReferenceEquals
directement).
La question et la réponse sont une question de attente du développeur , ce n'est pas une exigence technique.
[~ # ~] si [~ # ~] vous considérez qu'un wrapper n'a pas d'identité et qu'il soit défini uniquement par son contenu, alors la réponse à votre question est oui.
Mais c'est un problème récurrent. Deux wrappers doivent-ils présenter l'égalité lorsqu'ils enveloppent des objets différents mais avec les deux objets ayant exactement le même contenu?
La réponse se répète. [~ # ~] si [~ # ~] les objets de contenu n'ont pas d'identité personnelle et sont plutôt purement définis par leur contenu, alors les objets de contenu sont en fait des wrappers qui présenteront l'égalité. Si vous encapsulez ensuite les objets de contenu dans un autre wrapper, ce wrapper (supplémentaire) doit alors également présenter l'égalité.
C'est tortues tout le long .
Chaque fois que vous vous écartez du comportement par défaut, cela doit être explicitement documenté. En tant que développeur, je m'attends à ce que deux types de référence ne présentent pas l'égalité même si leur contenu est égal. Si vous changez ce comportement, je vous suggère de le documenter clairement afin que tous les développeurs soient conscients de ce comportement atypique.
Si je comprends bien le c # idiomatique, l'opérateur
==
Est pour l'égalité de référence tandis que la méthodeEquals()
est pour l'égalité des valeurs.
C'est son comportement par défaut, mais ce n'est pas une règle inamovible. C'est une question de convention, mais les conventions peuvent être modifiées lorsque cela se justifie .
string
est un excellent exemple ici, car ==
est également un contrôle d'égalité de valeur (même lorsqu'il n'y a pas de chaîne interne!). Pourquoi? En termes simples: car avoir des chaînes se comportent comme des objets de valeur semble plus intuitif pour la plupart des développeurs.
Si votre base de code (ou la vie de vos développeurs) peut être considérablement simplifiée en faisant en sorte que vos wrappers présentent une égalité de valeur à tous les niveaux, allez-y (mais documentez-le ).
Si vous jamais avez besoin de vérifications d'égalité de référence (ou qu'elles sont rendues inutiles par votre domaine d'activité), alors il n'y a aucun intérêt à conserver une vérification d'égalité de référence. Il est préférable de le remplacer par une vérification de l'égalité des valeurs afin d'éviter les erreurs de développement .
Cependant, sachez que si vous avez besoin de vérifier l'égalité des références plus tard, la réimplémenter peut prendre un effort notable.
Vous comparez essentiellement des chaînes, donc je serais étonné que deux wrappers contenant le même contenu XML ne soient pas considérés comme égaux, que ce soit vérifié en utilisant Equals ou ==.
La règle idiomatique peut avoir un sens pour les objets de type référence en général, mais les chaînes sont spéciales dans un sens idiomatique, vous êtes censé les traiter et les considérer comme des valeurs bien que techniquement ce soient des types référence.
Votre suffixe Wrapper ajoute cependant de la confusion. Il dit essentiellement "pas un élément XML". Dois-je donc le traiter comme un type de référence après tout? Sémantiquement, cela n'aurait aucun sens. Je serais moins confus si la classe était nommée XmlContent. Cela signifierait que nous nous soucions du contenu, pas des détails techniques de mise en œuvre.