Является ли это ожидаемым поведением С# 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();
}