我已经在类中使用以下代码实现了IEquatable接口。
public bool Equals(ClauseBE other)
{
if (this._id == other._id)
{
return true;
}
return false;
}
public override bool Equals(Object obj)
{
if (obj == null)
{
return base.Equals(obj);
}
if (!(obj is ClauseBE))
{
throw new InvalidCastException("The 'obj' argument is not a ClauseBE object.");
}
return Equals(obj as ClauseBE);
}
public override int GetHashCode()
{
return this._id.GetHashCode();
}
public static bool operator ==(ClauseBE a, ClauseBE b)
{
// cast to object so we call the overloaded Equals function which appropriately checks when b is null.
return a.Equals(b as object);
}
public static bool operator !=(ClauseBE a, ClauseBE b)
{
// cast to object so we call the overloaded Equals function which appropriately checks when b is null.
return !a.Equals(b as object);
}
此代码适用于大多数情况。但是,以下检查会在等于运算符重载方法中抛出异常,因为a为null,因此没有Equals方法。
if(this.Clause != null)
{
}
解决此问题的标准方法是什么?
我已经去过这个,但看起来很麻烦。我希望有更优雅的方法来实现这一目标。
public static bool operator ==(ClauseBE a, ClauseBE b)
{
if (a as object == null && b as object == null)
{
return true;
}
if ((a as object == null && b as object != null)
|| (b as object == null && a as object != null))
{
return false;
}
// cast to object so we call the overloaded Equals function which appropriately checks when b is null.
return a.Equals(b as object);
}
public static bool operator !=(ClauseBE a, ClauseBE b)
{
if (a as object == null && b as object == null)
{
return false;
}
if((a as object == null && b as object != null)
|| (b as object == null && a as object != null))
{
return true;
}
// cast to object so we call the overloaded Equals function which appropriately checks when b is null.
return !a.Equals(b as object);
}
谢谢大家。我从每个人那里得到了很多好的建议,我真的很感激。这是我最终确定的,它比我开始时更优雅。除运算符重载外,所有代码都相同。
public static bool operator ==(ClauseBE a, ClauseBE b)
{
if (ReferenceEquals(a, null) && ReferenceEquals(b, null))
{
return true;
}
if (ReferenceEquals(a, null) || ReferenceEquals(b, null))
{
return false;
}
return a.Equals(b);
}
public static bool operator !=(ClauseBE a, ClauseBE b)
{
return !(a == b);
}
答案 0 :(得分:5)
我总是发现使用null处理编写静态运算符更容易,并且使用Equals覆盖调用重载运算符,并将“this”作为参数之一。
来自Guidelines for Overloading Equals() and Operator == (C# Programming Guide)
//add this code to class ThreeDPoint as defined previously
//
public static bool operator ==(ThreeDPoint a, ThreeDPoint b)
{
// If both are null, or both are same instance, return true.
if (System.Object.ReferenceEquals(a, b))
{
return true;
}
// If one is null, but not both, return false.
if (((object)a == null) || ((object)b == null))
{
return false;
}
// Return true if the fields match:
return a.x == b.x && a.y == b.y && a.z == b.z;
}
public static bool operator !=(ThreeDPoint a, ThreeDPoint b)
{
return !(a == b);
}
答案 1 :(得分:2)
这就是ReSharper创建相等运算符和实现IEquatable<T>
的方式,我当然盲目相信; - )
public class ClauseBE : IEquatable<ClauseBE>
{
private int _id;
public bool Equals(ClauseBE other)
{
if (ReferenceEquals(null, other))
return false;
if (ReferenceEquals(this, other))
return true;
return other._id == this._id;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
return false;
if (ReferenceEquals(this, obj))
return true;
if (obj.GetType() != typeof(ClauseBE))
return false;
return Equals((ClauseBE)obj);
}
public override int GetHashCode()
{
return this._id.GetHashCode();
}
public static bool operator ==(ClauseBE left, ClauseBE right)
{
return Equals(left, right);
}
public static bool operator !=(ClauseBE left, ClauseBE right)
{
return !Equals(left, right);
}
}
答案 2 :(得分:1)
检查null并返回false。如果其中一个操作数为null,则Equals应始终为false;
答案 3 :(得分:1)
我认为这比在检查null之前转换为Object有点麻烦:
ReferenceEquals(a, null)
答案 4 :(得分:1)
其他答案为一般问题提供了很好的解决方案。
但是,您自己的代码可以简化为一个相对简单的解决方案......
首先,在您的==
运算符开始时,您有:
// First test
if (a as object == null && b as object == null)
{
return true;
}
这有资格“工作太辛苦”。
如果ClauseBE
是引用类型,那么您只需要与null
进行比较 - “as object
”是多余的;同样,如果ClauseBE
是值类型,那么它永远不会是null
。
假设ClauseBE
是引用类型(最可能的情况),那么你可以简化到这一点 - 注意我们使用Object.Equals()
来避免无限递归和堆栈井喷。
// First test
if (Object.Equals(a, null) && Object.Equals(b, null))
{
return true;
}
一个有用的捷径是使用Object.ReferenceEquals()
- 为你处理空值。
所以你可以这样写:
// First test
if (Object.ReferenceEquals(a, b))
{
return true;
}
奖励,这也可以处理a
和b
是完全相同的对象的情况。
完成Object.ReferenceEquals()
测试后,您知道a
和b
不同。
所以你的下一个测试:
// Second test
if ((a as object == null && b as object != null)
|| (b as object == null && a as object != null))
{
return false;
}
可以简化 - 因为您知道如果a
为空,b
不能为空,依此类推。
// Second test
if (Object.Equals(a, null) || Object.Equals(b, null))
{
return false;
}
如果此测试失败,那么您知道a
和b
不同,并且两者都不为空。是打电话给被覆盖的Equals()
的好时机。
// Use the implementation of Equals() for the rest
return a.Equals(b as object);
答案 5 :(得分:0)
public class Foo : IEquatable<Foo>
{
public Int32 Id { get; set; }
public override Int32 GetHashCode()
{
return this.Id.GetHashCode();
}
public override Boolean Equals(Object obj)
{
return !Object.ReferenceEquals(obj as Foo, null)
&& (this.Id == ((Foo)obj).Id);
// Alternative casting to Object to use == operator.
return ((Object)(obj as Foo) != null) && (this.Id == ((Foo)obj).Id);
}
public static Boolean operator ==(Foo a, Foo b)
{
return Object.Equals(a, b);
}
public static Boolean operator !=(Foo a, Foo b)
{
return !Object.Equals(a, b);
}
public Boolean Equals(Foo other)
{
return Object.Equals(this, other);
}
}
答案 6 :(得分:0)
我使用了以下方法,它似乎对我有用。事实上,Resharper建议采用这种方法。
public bool Equals(Foo pFoo)
{
if (pFoo == null)
return false;
return (pFoo.Id == Id);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(obj, this))
return true;
return Equals(obj as Foo);
}
答案 7 :(得分:0)
我更喜欢在Equals(T)方法中执行所有比较逻辑,并在运算符重载框架中保留“if this或that null,else ...”。
覆盖运算符重载的唯一棘手的问题是,您不能再在Equals实现中使用这些运算符,例如与null
进行比较。相反,object.ReferenceEquals
可用于实现相同的效果。
在MSDN Guidelines for Overriding Equals() and Operator ==文章中的TwoDPoint示例之后,这是我在为类型实现值相等时生成的模式:
public override bool Equals( object obj ) {
// Note: For value types, would use:
// return obj is TwoDPoint && this.Equals( (TwoDPoint)obj );
return this.Equals( obj as TwoDPoint );
}
public bool Equals( TwoDPoint other ) {
// Note: null check not needed for value types.
return !object.ReferenceEquals( other, null )
&& EqualityComparer<int>.Default.Equals( this.X, other.X )
&& EqualityComparer<int>.Default.Equals( this.Y, other.Y );
}
public static bool operator ==( TwoDPoint left, TwoDPoint right ) {
// System.Collections.Generic.EqualityComparer<T> will perform the null checks
// on the operands, and will call the Equals overload if necessary.
return EqualityComparer<TwoDPoint>.Default.Equals( left, right );
}
public static bool operator !=( TwoDPoint left, TwoDPoint right ) {
return !EqualityComparer<TwoDPoint>.Default.Equals( left, right );
}
上面的表单是最安全的实现,因为它只是将字段相等性检查转发给框架,并且不需要知道字段是否重载了相等运算符。在您知道存在过载的情况下简化此操作是完全可以的:
public bool Equals( TwoDPoint other ) {
return !object.ReferenceEquals( other, null )
&& this.X == other.X
&& this.Y == other.Y;
}
您还可以在比较引用类型时,或者当装箱值类型无关紧要时,通过调用静态EqualityComparer<T>
方法替换运算符重载中的object.Equals
调用:
public static bool operator ==( TwoDPoint left, TwoDPoint right ) {
return object.Equals( left, right );
}
public static bool operator !=( TwoDPoint left, TwoDPoint right ) {
return !object.Equals( left, right );
}
另请参阅What is the best algorithm for an overridden GetHashCode?了解GetHashCode
。