C# 为什么在此运算符定义中抛出stackoverflowexception?
请在下面的代码中查看我的评论。如何检查参数是否为C# 为什么在此运算符定义中抛出stackoverflowexception?,c#,.net,operator-overloading,stack-overflow,C#,.net,Operator Overloading,Stack Overflow,请在下面的代码中查看我的评论。如何检查参数是否为null?看起来null被强制转换为Foo,这实际上是对=操作符进行递归调用。为什么会发生这种情况 public class Foo { public static bool operator ==(Foo f1, Foo f2) { if (f1 == null) //This throw a StackOverflowException return f2 == null;
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;
}
}
为什么会发生这种情况
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;
}
}
因为语言规则是这样说的
您已向操作员提供此签名:
public static bool operator ==(Foo f1, Foo f2)
然后,无论代码中出现什么情况,都会得到以下表达式:
f1 == null
其中f1
的编译时类型为Foo
。现在null
也可以隐式转换为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;
}
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
}
}
//如果可能,请在密封类型上定义相等。
//否则会变得更混乱。。。
公共密封类Foo:IEquatable
{
公共静态布尔运算符==(Foo f1,Foo f2)
{
if(object.ReferenceEquals(f1,f2))
{
返回true;
}
if(object.ReferenceEquals)(f1,null)||
object.ReferenceEquals(f2,null))
{
返回false;
}
//在此处执行实际相等性检查
}
公共覆盖布尔等于(对象其他)
{
返回this==(其他为Foo);
}
公共布尔等于(Foo其他)
{
返回this==other;
}
公共静态布尔运算符!=(Foo f1,Foo f2)
{
返回!(f1==f2);
}
公共覆盖int GetHashCode()
{
//在这里计算哈希代码
}
}
请注意,这只允许您在一个位置进行空性检查。为了避免在通过
Equals
实例方法调用f1
时对null进行冗余比较,在检查f1
的null值后,您可以将=
委托给Equals
,但我可能会坚持这一点,所以它可能会帮助一些人。
为了保持完全相同的行为,但没有堆栈溢出,我现在将其重写如下:
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;
}
}
在==运算符的实现中使用==运算符,并且它位于同一类型上。这里需要的是Object.ReferenceEquals来测试null。如果不是
Foo
,您希望null
在该=
操作中是什么类型?@harold:我在null
上使用它,而不是在同一类型上使用它。@Ropstah左边的操作数使该类型与thinkrelated有关:(workkaround上的线程)谢谢你的详细回复@Jon Skeet,感谢您不仅仅是写了一个简短的回复,并展示了一个完整的示例,非常感谢。解释得很好!不幸的是,要想被这个错误抓住,一个很好的方法就是遵循同样的错误行为…:(如果f1
和f2
都为空怎么办?您的代码将返回false
。您不应该检查if(object.ReferenceEquals(f1,null)和&!object.ReferenceEquals(f2,null))
反之亦然?@DrewChapin:不会-它将返回true,因为if(object.ReferenceEquals(f1,f2)){返回true;}