Аргументы перегруженного оператора всегда нулевые

Итак, у меня есть класс, который переопределяет Equals(object obj) и GetHashCode() вместе с реализацией IEquatable. Чтобы сделать работу с этим типом более естественной при проверке на равенство, я подумал, черт возьми, я бы перегрузил оператор равенства и оператор неравенства, не беспокойтесь...

Э-э, беспокоюсь... рассмотрим следующее - где оба экземпляра myType НЕ являются нулевыми:

if (myType != container.myType) //NullReferenceException
{
    //never get here
}
//never get here either

Теперь контейнер — это просто еще один класс для хранения экземпляра myType среди прочего, который используется для кэширования элементов.

Вот фактический (соответствующий) код из myType:

public class MyType : IEquatable<MyType>
{
    public static bool operator ==(MyType myTypeA, MyType myTypeB)
    {
        return myTypeA.Equals(myTypeB);
    }

    public static bool operator !=(MyType myTypeA, MyType myTypeB)
    {
        return !(myTypeA == myTypeB);
    }

    public override bool Equals(object obj)
    {
        if (obj != null && obj is MyType)
        {
            return Equals((MyType)obj);
        }
        return false;
    }

    public bool Equals(MyType other)
    {
        if (other != null)
        {
            return other.ToString() == ToString();
        }
        return false;
    }
}

Есть ли опыт на этом фронте?

Спасибо.


person Grant Thomas    schedule 03.10.2010    source источник
comment
Можете ли вы опубликовать трассировку стека? Потому что, если вы получаете NRE, что-то должно быть нулевым. Как вы проверили, что оба аргумента не равны нулю?   -  person Kirk Woll    schedule 03.10.2010


Ответы (3)


Хитрость... проблема в том, что вы используете оператор равенства внутри переопределения Equal следующим образом:

public bool Equals(MyType other)
{
    if (other != null)

Он переходит к вашему перегруженному оператору !=, который, в свою очередь, переходит к вашему оператору ==, который пытается сделать null.Equals...

person Amittai Shapira    schedule 03.10.2010
comment
Это распространенная ошибка. Либо преобразовать операнды (или только один из них) в object, как в if ((object)other != (object)null) { ... }, либо использовать object.ReferenceEquals, который является статическим (следовательно, никогда не переопределяется). - person Jeppe Stig Nielsen; 24.07.2013

Пара указателей -

  1. Если вы переопределили == и != в классах, обязательно используйте ReferenceEquals для проверки нулевого значения внутри реализаций перегрузки, а не ==, так как это вызовет ваш перегруженный оператор и либо войдет в цикл, либо попытается вызвать Equals для нулевого значения this ссылка, что, вероятно, здесь и происходит.
  2. Не переопределяйте == и != в классах. Эти операторы предназначены для равенства значений, а классы на самом деле не предназначены для обеспечения равенства значений. Либо удалите перегрузки операторов, либо сделайте MyType структурой.
person thecoop    schedule 03.10.2010
comment
Спасибо, это и еще немного терпения в пошаговом выполнении кода, не пропуская вызов, позволили мне понять, что Equals на самом деле вызывался с нулем в качестве сравнения, которое приводило к вызову оператора равенства с нулем, что приводило к к другому вызову Equals с нулевым значением, что привело к другому вызову оператора равенства с двумя нулевыми значениями и BANG. Могло быть и хуже, могла быть ужасная петля. - person Grant Thomas; 03.10.2010

Как заявили другие, вам нужно тщательно проверять наличие нулей, поскольку он снова вызовет вашу функцию равенства, что обычно приводит к исключению StackOverflowException.

Когда я использую интерфейс IEquatable для классов, я обычно использую следующий код:

public override bool Equals(object obj)
{
    // If obj isn't MyType then 'as' will pass in null
    return this.Equals(obj as MyType);
}

public bool Equals(MyType other)
{
    if (object.ReferenceEquals(other, null))
    {
        return false;
    }

    // Actual comparison code here
    return other.ToString() == this.ToString();
}
person Samuel    schedule 03.10.2010