C#重载运算符==和!=

3

我在这几个类和接口中无法得到所需的行为。

以下是我的问题,

//Inside a Unit Test that has access to internal methods and properties

INode  firstNode, secondNode;

INodeId  id = new NodeId (4);

first = new Node (id, "node");
second = new Node (id, "node");

Assert.IsTrue (first == second);

上述断言失败的原因是似乎调用了对象类的equals方法,而不是Node和NodeId类中重载的运算符。
如果您对如何获得所需行为有任何建议,那将非常棒。
这是我正在工作的框架的一部分:
public interface IIdentifier<T> where T : class
{
    TKeyDataType GetKey<TKeyDataType> ();

    bool Equals (IIdentifier<T> obj;
}

public interface INode
{
    string name
    {
        get;
    }

    INodeId id
    {
        get;
    }
}

public interface INodeId : IIdentifier<INode>
{
}

public class Node : INode
{
    internal Node(INodeId  id, string name)
    { 
       //Work
    }

    public static bool operator == (Node n1, Node n2)
    {
        return n1.equals(n2);
    }

    public static bool operator != (Node n1, Node n2)
    {
        return !n1.equals(n2);
    }

    public bool Equals (INode  node)
    {
        return this.name == node.name &&
               this.id = node.id;
    }

    #region INode Properties

    }

public class NodeId : INodeId
{

    internal NodeId(int id)
    { 
       //Work
    }

    public static bool operator == (NodeId  n1, NodeId  n2)
    {
        return n1.equals(n2);
    }

    public static bool operator != (NodeId  n1, NodeId  n2)
    {
        return !n1.equals(n2);
    }

    public override bool Equals (object obj)
    {
        return this.Equals ((IIdentifier<INode>) obj);
    }

    public bool Equals (IIdentifier<INode> obj)
    {
        return obj.GetKey<int>() ==  this.GetKey<int>();
    }

    public TKeyDataType GetKey<TKeyDataType> ()
    {
        return (TKeyDataType) Convert.ChangeType (
            m_id,
            typeof (TKeyDataType),
            CultureInfo.InvariantCulture);
    }


    private int m_id;

}

正确地实现相等性是非常棘手的。您可能会对这些笔记感兴趣,其中涉及了不同计算相等性方式的设计考虑:http://blogs.msdn.com/ericlippert/archive/2009/04/09/double-your-dispatch-double-your-fun.aspx - Eric Lippert
顺便提一下,您可能还想考虑重写 GetHashCode() 方法。对于这种情况,NodeId 只需返回 m_id.GetHashCode(),而 Node 则需要计算成员字段的组合。Jon Skeet 在这里有一个很棒的实现:https://dev59.com/EnVC5IYBdhLWcg3wihqv#263416 - Jesse C. Slicer
3个回答

6
运算符重载是根据操作数的声明类型在编译时解析的,而不是在运行时根据对象的实际类型解析的。换句话说,运算符重载不是虚拟的。因此,您上面执行的比较是 INode.operator==,而不是 Node.operator==。由于没有定义 INode.operator==,所以重载解析为 Object.operator==,它只进行引用比较。
这方面没有真正好的解决方法。最正确的做法是在操作数可能是对象的任何地方使用 Equals() 而不是 ==。如果您真的需要一个虚拟的运算符重载,您应该在您的对象继承的根基类中定义 operator ==,并让该重载调用 Equals。但请注意,这对于接口是无效的,这就是您所拥有的。

嘿,感谢帮助。这肯定会让我朝着正确的方向前进。 - Meiscooldude
请注意,出于答案中概述的相同原因,即使您调用first.Equals(second),编译器也会选择Object.Equals而不是Node.Equals - Fredrik Mörk
@Fredrik:Equals是虚拟的,因此在实际运行时类型上调用的方法将是最高派生的Equals实现。 - JSBձոգչ
@JS:是的。我觉得很奇怪,因为编译器在尝试给出的代码示例时选择了Object.Equals,但后来我意识到Node实际上并没有重写Object.Equals方法。 - Fredrik Mörk

1

我认为你可能需要像在NodeId中一样覆盖Node的Equals(object)方法。所以:

public override bool Equals (object obj)
{
    if (obj is Node)
    {
        return this.Equals(obj as Node);
    }
    return false;
}

// your code (modified to take a Node instead of an INode)
public bool Equals (Node  node)
{
    return this.name == node.name &&
           this.id = node.id;
}

这不正确(因为Node不是密封的)。如果一个类继承自Node,那么Equals()的反身性将会丢失。你需要检查obj和this的确切类型。 - Bryan
@Mark:你缺少了override关键字:public override bool Equals (object obj)。除此之外,这个答案解决了OP所问的问题。 - Fredrik Mörk
@Brian - 是的,你说得对,我复制并粘贴了“// your code”下面的代码,没有意识到它正在使用INode而不是Node。我会更新帖子的。谢谢! - Mark Synowiec

0

这里使用了对象的 == 运算符,因为 firstNode 和 secondNode 不属于 Node 类型,它们属于 INode 类型。编译器无法识别底层类型。

由于接口中不能重载运算符,您最好重写代码,以便不使用 INode 接口。


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