请在下面的代码中查看我的评论。我应该如何检查参数是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;
}
}
答案 0 :(得分:26)
为什么会这样?
因为语言规则说。
您已向运营商提供此签名:
public static bool operator ==(Foo f1, Foo f2)
然后 - 无论发生在代码中的哪个地方 - 你都有这个表达式:
f1 == null
其中f1
的编译时类型为Foo
。现在null
也可隐式转换为Foo
,那么为什么不会使用您的运算符?如果你的操作员的第一行无条件地调用自己,你应该期待堆栈溢出......
为了让不发生,您需要对该语言进行两项更改之一:
==
在==
声明中使用时的含义。 ICK。==
表达式,其中一个操作数为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);
}
但是,您然后需要修复您的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
}
}
请注意,这使您只能在一个地方进行无效检查。为了避免在f1
通过Equals
的实例方法调用时对{* 1}}进行冗余比较,您可以从==
委托给{{} 1}}检查Equals
的无效后,但我可能会坚持这一点。
答案 1 :(得分:0)
一个有点老的话题,但是我到达了此页面,因此它可能对某些人有所帮助。 为了保持完全相同的行为,但没有堆栈溢出,我现在将其重写如下:
public class Foo
{
public static bool operator ==(Foo f1, Foo f2)
{
if (f1 is null)
return f2 is null;
if (f2 is null)
return false;
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 is null) return false;
return f == this;
}
public override int GetHashCode()
{
return 0;
}
}