Я пишу оболочку для элементов XML, которая позволяет разработчику легко анализировать атрибуты из XML. Оболочка не имеет никакого состояния, кроме объекта, который оборачивается.
Я рассматриваю следующую реализацию (упрощенную для этого примера), которая включает перегрузку для ==
оператора.
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);
}
}
Как я понимаю, идиоматический C #, ==
оператор для равенства ссылок, а Equals()
метод для равенства значений. Но в этом случае «значение» - это просто ссылка на обертываемый объект. Так что мне не ясно, что является обычным или идиоматическим для C #.
Например, в этом коде ...
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.");
}
.... должна ли программа выводить "Обертки a и b одинаковы"? Или это будет странно, то есть нарушит принцип наименьшего удивления ?
Equals
я никогда не переживал==
(но никогда не наоборот). Ленивый идиоматик? Если я получаю другое поведение без явного приведения, который нарушает наименьшее удивление.Ответы:
Поскольку ссылка на завернутый
XElement
является неизменной, внешне заметных различий между двумя экземплярами одного иXmlWrapper
того же элемента нет, поэтому имеет смысл перегрузить,==
чтобы отразить этот факт.Код клиента почти всегда заботится о логическом равенстве (которое по умолчанию реализуется с использованием равенства ссылок для ссылочных типов). Тот факт, что в куче есть два экземпляра, является деталью реализации, о которой не должны заботиться клиенты (и те, которые будут использовать
Object.ReferenceEquals
напрямую).источник
Если вы думаете, это имеет смысл
Вопрос и ответ - вопрос ожидания разработчика , это не техническое требование.
Если вы считаете, что обертка не имеет идентификатора и ее можно определить исключительно по ее содержимому, тогда ответ на ваш вопрос - да.
Но это постоянная проблема. Должны ли два обертки демонстрировать равенство, когда они обертывают разные объекты, но оба объекта имеют одинаковое содержимое?
Ответ повторяется. Если объекты контента не имеют персональной идентичности и вместо этого определяются исключительно своим контентом, то объекты контента фактически являются обертками, которые будут демонстрировать равенство. Если затем вы оберните объекты содержимого в другую оболочку, эта (дополнительная) оболочка должна также продемонстрировать равенство.
Это черепахи все время вниз .
Общий совет
Всякий раз, когда вы отклоняетесь от поведения по умолчанию, это должно быть явно задокументировано. Как разработчик, я ожидаю, что два ссылочных типа не будут демонстрировать равенство, даже если их содержание одинаково. Если вы измените это поведение, я бы предложил вам четко документировать его, чтобы все разработчики знали об этом нетипичном поведении.
Это поведение по умолчанию, но это не является неизменным правилом. Это вопрос соглашения, но условные обозначения могут быть изменены там, где это оправдано .
string
отличный пример здесь, также как==
и проверка на равенство значений (даже если нет интернирования строк!). Почему? Проще говоря: потому что строки ведут себя как объекты значений, они кажутся более интуитивными для большинства разработчиков.Если ваша кодовая база (или жизнь ваших разработчиков) может быть значительно упрощена, если ваши обертки демонстрируют равенство значений по всем направлениям, сделайте это (но задокументируйте это ).
Если вам никогда не требуются проверки на равенство ссылок (или они становятся бесполезными в вашей бизнес-сфере), тогда нет смысла хранить проверку на равенство ссылок. Лучше заменить его проверкой на равенство значений, чтобы предотвратить ошибку разработчика .
Тем не менее, имейте в виду, что если вам понадобятся проверки на равенство ссылок позже, повторная реализация может потребовать значительных усилий.
источник
==
проверяет равенство ссылок, так как это поведение по умолчанию. Однако, если на==
самом деле проверяется равенство значений, я ожидаю (то есть требую), что это задокументировано явно.I'm curious why you expect that reference types won't define content equality.
Они не определяют его по умолчанию , но это не значит, что это невозможно сделать. Я никогда не говорил, что это не может (или не должно) быть сделано, я просто не ожидаю (то есть предполагаю) это по умолчанию.Вы в основном сравниваете строки, поэтому я был бы удивлен, если бы две обертки, содержащие один и тот же контент XML, не считались равными, будь то проверка с использованием Equals или ==.
Идиоматическое правило может иметь смысл для объектов ссылочного типа в целом, но строки являются особенными в идиоматическом смысле, вы должны рассматривать и рассматривать их как значения, хотя технически они являются ссылочными типами.
Ваш постфикс Wrapper добавляет путаницу. В основном это говорит "не элемент XML". Так должен ли я рассматривать его как ссылочный тип в конце концов? Семантически это не имеет смысла. Я был бы менее смущен, если бы класс назывался XmlContent. Это будет означать, что мы заботимся о контенте, а не о технических деталях реализации.
источник