当重载等号运算符时,处理空值的最佳方法是什么?

4

可能是重复问题:
如何在“==”运算符重载中检查空值而不导致无限递归?

假设我有一个像这样的类型:

public class Effect
{
    public static bool operator == ( Effect a, Effect b )
    {
        return a.Equals ( b );
    }

    public static bool operator != ( Effect a, Effect b )
    {
        return !a.Equals ( b );
    }

    public bool Equals ( Effect effect )
    {
        return this.TypeID.Equals ( effect.TypeID );
    }

    public override bool Equals ( object obj )
    {
        return this.TypeID.Equals ( ( ( Effect ) obj ).TypeID );
    }
}

什么是处理空值最健壮和最干净的方法?
我不确定是否需要同时检查当前实例(this)和传递的实例(effect/obj)是否为空?如果当前实例(this)为空,编译器是否仍会调用effect.Equals或Object.Equals?
此外,无论哪种方式,空值检查应该在哪里进行?我假设只在Equals方法内部进行,而不是在等式运算符(==,!=)中进行。

3
用物体属性的相等来代替两个对象之间的相等,这样真的是一个好主意吗?我可以看到在结构体中这样做是可行的,但对于类覆盖“==”运算符似乎容易产生难以发现的错误。 - Ilia G
1
@liho1eye,@Joan:微软有一条指南,对于可变类型来说,重载==Equals不是一个好主意。 - H H
1
@liho1eye:字符串是一种引用类型(也是一种集合)。基于值的相等性对其起作用。 - H H
1
@Henk Holterman,字符串也是不可变的 :) - Cor_Blimey
@JoanVenge 刚刚将标题从“覆盖”更正为“重载”。 - nawfal
显示剩余4条评论
6个回答

2

Visual Studio提供了一种代码片段,为您提供了一个基本的Equals()实现。除非您有充分的理由不这样做,否则我建议您遵循这个方法。

// override object.Equals
public override bool Equals(object obj)
{
    //       
    // See the full list of guidelines at
    //   http://go.microsoft.com/fwlink/?LinkID=85237  
    // and also the guidance for operator== at
    //   http://go.microsoft.com/fwlink/?LinkId=85238
    //

    if (obj == null || GetType() != obj.GetType())
    {
        return false;
    }

    // TODO: write your implementation of Equals() here
    throw new NotImplementedException();
    return base.Equals(obj);
}

// override object.GetHashCode
public override int GetHashCode()
{
    // TODO: write your implementation of GetHashCode() here
    throw new NotImplementedException();
    return base.GetHashCode();
}

这不回答问题。请纠正或删除它。 - nawfal
我相信它回答了这个问题。对于“如何处理空值”的问题,这段代码片段的回答是“返回false”。 - David Yaw
抱歉我说话有些严厉,我的错,但是问题的关键在于处理运算符重载中的空值,这也是问题所在。即使操作者像你一样做了,仍然可能在重载的 ==!= 中出现空引用异常。 - nawfal

2

首先,this永远不可能是null,至少在C#编译器生成的代码中不会出现。

其次,使用ReferenceEquals方法来检查null引用,避免调用重载版本的==(或者使用((object) sometypeinstance) == null)。


谢谢,但是如果我有Effect e = null,那么在这种情况下它的“this”值不是也为null吗? - Joan Venge
1
+1,这里只有一个正确答案,认真对待! - nawfal

1

怎么样?

public static bool operator == ( Effect a, Effect b )     
{  
    return object.Equals(a, b);
}

Object.Equals() 的默认实现会为您进行空值检查。

如果您好奇,这是 Object.Equals() 如何执行的(来自 .NET Reflector):

public static bool Equals(object objA, object objB)
{
    return ((objA == objB) || (((objA != null) && (objB != null)) && objA.Equals(objB)));
}

谢谢,当我使用了空值时,我的类型抛出了一个空引用异常。 - Joan Venge
请查看您的问题的注释 - 覆盖 == 和!= 不是轻易做的事情 :) - Doug
谢谢,我没有注意到你信息的前半部分。 - Joan Venge

0

在编写类时,每个带参数的方法都需要检查 null 值。

public class Effect
{
    public static bool operator == ( Effect a, Effect b )
    {
        if (a == null) && (b == null) return true;
            if (a == null) return false;
            return a.Equals ( b );
    }

    public static bool operator != ( Effect a, Effect b )
    {
        return !(a == b);
    }

    public bool Equals ( Effect effect )
    {
            if (b == null) return false;
        return this.TypeID.Equals ( effect.TypeID );
    }

    public override bool Equals ( object obj )
    {
            if (obj == null) return false;
        return this.TypeID.Equals ( ( ( Effect ) obj ).TypeID );
    }
}

需要注意的其他事项包括实现GetHashCode(如果正在实现相等性),以及GetHashCode只应在不可变属性上实现,如果此对象将用于使用哈希码比较项的字典或类似对象中。

谢谢,这个代码不会编译通过,对吧?(return !a == b;) - Joan Venge
我的错误,我错过了一对括号,我可能太习惯于使用IDE这个工具了。我已经将它更改为!(a == b); - Spencer Booth
没关系,我对IDE太熟悉了 :O - Joan Venge
-1,这会创建无限递归,并且根本不起作用。请参见https://dev59.com/XXVD5IYBdhLWcg3wI4CM - nawfal

-1

添加这个:

 public static bool operator == ( Effect a, Effect b )     
 {  
     return a is Effect && b is Effect && a.TypeID.Equals (b.TypeID);    
 }  
 public static bool operator != ( Effect a, Effect b )
 {         
     return !(a == b );     
 }      
 public bool Equals ( Effect effect )     
 {         
    return this == effect );     
 }      
 public override bool Equals ( object obj )     
 {   
    return obj is Effect && this == obj); 
 } 

或者将最后一个替换为:

 public override bool Equals ( object obj )     
 {   
    if (obj == null) throw new ArgumentNullException(
         "obj", "obj is null");
    if (!(obj is effect)) throw new ArgumentException(
         "obj", "obj is not an effect");
    return obj is Effect && this == obj); 
 }

1
不过要小心,== 运算符需要你同时定义 !=。这可能会导致相互递归的问题。 - Dan Tao
1
如果您使用该代码,就必须添加相应的"!="方法。 - Spencer Booth
2
这会引起堆栈溢出,不是吗?== 调用!=,!= 调用 ==,等等... - Chris Shain
1
@Charles:克里斯是对的,这将会溢出。 - Dan Tao
是的,正在修复它...试图过于迅速的危险。 - Charles Bretana

-1

是的,你应该检查 null。为什么害怕呢?

记住,当你使用 equals 方法时,可能也需要查看 hashcode 方法!这两个方法是相互关联的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接