为什么在此运算符定义中抛出stackoverflowexception?

Rop*_*tah 8 .net c# stack-overflow operator-overloading

请在下面的代码中查看我的评论.我应该如何检查参数null?它看起来像null被转换为Foo基本上使递归调用==操作符.为什么会这样?

public class Foo
{
    public static bool operator ==(Foo f1, Foo f2)
    {
        if (f1 == null) //This throw a StackOverflowException
            return f2 == null;
        if (f2 == null)
            return f1 == null;
        else
            return f1.Equals((object)f2);
    }

    public static bool operator !=(Foo f1, Foo f2)
    {
        return !(f1 == f2);
    }

    public override bool Equals(object obj)
    {
        Foo f = obj as Foo;
        if (f == (Foo)null)
            return false;

        return false;
    }

    public override int GetHashCode()
    {
        return 0;
    }
}
Run Code Online (Sandbox Code Playgroud)

Jon*_*eet 27

为什么会这样?

因为语言规则说.

您已向运营商提供此签名:

public static bool operator ==(Foo f1, Foo f2)
Run Code Online (Sandbox Code Playgroud)

然后 - 无论发生在代码中的哪个地方 - 你都有这个表达式:

f1 == null
Run Code Online (Sandbox Code Playgroud)

其中f1编译时类型为Foo.现在null也可以隐式转换Foo,为什么使用你的运算符?如果你的操作员的第一行无条件地调用自己,你应该期待堆栈溢出......

为了发生这种情况,您需要对语言进行两项更改之一:

  • 该语言必须具有特殊情况==才能在声明中使用==.伊克.
  • 语言必须决定任何==带有一个操作数的表达式null 总是意味着参考比较.

IMO也不是特别好.避免它很简单,避免冗余,并添加优化:

public static bool operator ==(Foo f1, Foo f2)
{
    if (object.ReferenceEquals(f1, f2))
    {
        return true;
    }
    if (object.ReferenceEquals(f1, null) ||
        object.ReferenceEquals(f2, null))
    {
        return false;
    }
    return f1.Equals(f2);
}
Run Code Online (Sandbox Code Playgroud)

但是,你需要修正你的Equals方法,因为这则结束调用回你的==,导致另一堆栈溢出.你实际上从未真正说过你希望如何确定平等......

我通常会有这样的事情:

// Where possible, define equality on sealed types.
// It gets messier otherwise...
public sealed class Foo : IEquatable<Foo>
{
    public static bool operator ==(Foo f1, Foo f2)
    {
        if (object.ReferenceEquals(f1, f2))
        {
            return true;
        }
        if (object.ReferenceEquals(f1, null) ||
            object.ReferenceEquals(f2, null))
        {
            return false;
        }

        // Perform actual equality check here
    }

    public override bool Equals(object other)
    {
        return this == (other as Foo);
    }

    public bool Equals(Foo other)
    {
        return this == other;
    }

    public static bool operator !=(Foo f1, Foo f2)
    {
        return !(f1 == f2);
    }

    public override int GetHashCode()
    {
        // Compute hash code here
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,这使您只能在一个地方进行无效检查.为了避免f1在通过开始的实例方法调用null时冗余地比较null Equals,你可以在检查无效之后委托==给,但我可能会坚持这样做.Equalsf1