Является ли это ожидаемым поведением С# 4.0 Tuple?
Я вижу различное поведение между использованием .Equals и == между двумя .NET Tuple < > экземплярами .NET 4.0. Если бы я переопределил Equals на объекте в Tuple < > и вызовет .Equals в кортежах, будет вызван переопределение Equals. Если я использую == в Tuples, переопределение Equals не вызывается. Это по дизайну и имеет смысл?
EDIT: Из ответов и комментариев я могу сказать, что я не понимаю. Я знаю, что Tuple < > является ссылочным типом и что для ссылочных типов == проверяет идентификатор (ReferenceEquals). Но должен ли Tuple < > override == проверять равенство объектов, которые он содержит? Для согласованности, вероятно, нет.
Например, если у меня есть простой объект
public class NameAndNumber
{
public int Number { get; set; }
public string Name { get; set; }
public override bool Equals(object obj)
{
if (obj is NameAndNumber)
{
NameAndNumber other = (NameAndNumber)obj;
return Number == other.Number && Name == other.Name;
}
return false;
}
}
а затем я делаю что-то вроде этого:
Tuple<NameAndNumber, NameAndNumber> left = new Tuple<NameAndNumber, NameAndNumber>(
new NameAndNumber { Name = "one", Number = 1 },
new NameAndNumber { Name = "two", Number = 2 });
Tuple<NameAndNumber, NameAndNumber> right = new Tuple<NameAndNumber, NameAndNumber>(
new NameAndNumber { Name = "one", Number = 1 },
new NameAndNumber { Name = "two", Number = 2 });
bool operatorResult = left == right;
bool equalsResult = left.Equals(right);
Console.Out.WriteLine("operatorResult = {0} equalsResult = {1}",
operatorResult, equalsResult);
Я получаю operatorResult = false equalsResult = true
Должен ли я ожидать этого?
Я знаю, что реализация Equals on NameAndNumber не является "правильной", это просто упрощенный пример кода.
Я также попытался реализовать IEquatable, ==,!= и GetHashCode. Те же результаты.
Ответы
Ответ 1
Результаты, которые вы видите, связаны с компрометацией дизайна, теперь Tuples разделяются между F # и С#. Главное, что все Tuples действительно реализованы как ссылочные типы, что было не так очевидно.
Решение о том, должны ли кортежи выполнять глубокие или неглубокие проверки равенства, было перенесено на два интерфейса: IStructuralComparable
, IStructuralEquatable
. Обратите внимание, что эти 2 теперь также реализованы классом Array.
Ответ 2
Для ссылочного типа: == выполняет сравнение идентичности, то есть он вернет true только в том случае, если обе ссылки указывают на один и тот же объект. Хотя ожидается, что метод Equals() будет выполнять сравнение значений, то есть он вернет true, если ссылки указывают на эквивалентные объекты.
Для ссылочных типов, в которых == была перегружена НЕ, она сравнивает, ссылаются ли две ссылки на один и тот же объект
Ответ 3
По умолчанию оператор == проверяет ссылочное равенство, поэтому Да ожидается результат, который вы видите.
См. Рекомендации по переопределению равных() и операторов == (Руководство по программированию на С#):
В С# существуют два разных типа равенства: ссылочное равенство (также известный как идентичность) и равенство значений. Ценностное равенство понятное значение равенства: оно означает, что два объекта содержат одинаковые значения. Например, два целых числа при значении 2 имеют значение равенство. Ссылка означает равенство что нет двух объектов для сравнить.
Ответ 4
По умолчанию ==
(в классе) означает ссылочное равенство; то есть они являются одним и тем же экземпляром; что object.ReferenceEquals(x,y)
вернется.
Вы можете предоставить свои собственные операторы ==/!= для получения ожидаемого поведения - и когда вы переопределяете Equals
, важно также переопределить GetHashCode
(в противном случае вы прекратите использование в качестве ключа - Почему важно переопределить GetHashCode, если метод Equals переопределен на С#?:
public static bool operator == (NameAndNumber x, NameAndNumber y) {
if (x == null && y == null) return true;
if (x == null || y == null) return false;
return x.Number == y.Number && x.Name == y.Name;
// or if polymorphism is important: return x.Equals(y);
}
public static bool operator !=(NameAndNumber x, NameAndNumber y) {
return !(x == y); // lazy but works
}
public override int GetHashCode() {
return (Name == null ? 0 : Name.GetHashCode()) +
17 * Number.GetHashCode();
}