Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/opencv/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 不可变ID属性如何影响.NET平等性?_C#_Equality - Fatal编程技术网

C# 不可变ID属性如何影响.NET平等性?

C# 不可变ID属性如何影响.NET平等性?,c#,equality,C#,Equality,(抱歉设置太长。我保证这里有个问题。) 考虑一个类节点,该节点具有在构造时分配的不可变唯一ID。在持久化对象图时,除其他外,此ID用于序列化。例如,当一个对象被反序列化时,它将根据ID根据主对象图进行测试,以查找冲突并拒绝它 此外,节点仅由专用系统实例化,所有对它们的公共访问都通过INode接口完成 因此,我们有类似于这种常见模式的东西: interface INode { NodeID ID { get; } // ... other awesome stuff } cla

(抱歉设置太长。我保证这里有个问题。)

考虑一个类
节点
,该节点具有在构造时分配的不可变唯一ID。在持久化对象图时,除其他外,此ID用于序列化。例如,当一个对象被反序列化时,它将根据ID根据主对象图进行测试,以查找冲突并拒绝它

此外,
节点
仅由专用系统实例化,所有对它们的公共访问都通过
INode
接口完成

因此,我们有类似于这种常见模式的东西:

interface INode
{
    NodeID ID { get; }

    // ... other awesome stuff
}

class Node : INode
{
    readonly NodeID _id = NodeID.CreateNew();

    NodeID INode.ID { get { return _id; } }

    // ... implement other awesome stuff

    public override bool Equals(object obj)
    {
        var node = obj as INode;
        return ReferenceEquals(node, null) ? false : _id.Equals(node.ID);
    }

    public override int GetHashCode()
    {
        return _id.GetHashCode();
    }
}
我的问题围绕.NET的这些比较/平等相关功能:

  • IEquatable
  • IComparable
    /
    IComparable
  • 运算符==
    /
    运算符=
最后是问题。当实现像
节点这样的类时

  • 您还实现了上述哪些接口/操作符?为什么?
  • 是否从
    INode
    节点扩展
    IEquatable
    ?考虑到
    IEquatable
    似乎只通过运行时类型检查得到使用(主要在BCL中),从
    INode
    扩展是否有意义
  • 对于那些您确实实现的,您是只在类本身上实现它们,还是另外在ID上实现它们?
    • 例如,
      IEquatable
      是否也是
      节点的一部分
    • 您是否在
      Equals
      中测试作为节点ID的
      对象
在C#工作了十多年后,我很不好意思没有对这些接口和与之相关的最佳实践有透彻的了解


(请注意,我们的.NET系统是在95%C#,5%C++/CLI,0%VB(如果有区别的话)中构建的。)

您的
节点.Equals
方法实际上只是
节点ID.Equals
的包装器,因此您需要在这里实现真正的比较逻辑(我假设它类似于
myIntId==someOtherInt
)。否则,您将只使用从
对象继承的默认
.Equals
行为


如果我正确地假设你最低级别的id只是一个int,您可以实现
IComparable
IEquatable
,这将只是int-id字段的包装,因为
Int32
已经实现了
IComparable
IEquatable

我对它的理解可能还不完善,但接下来

我从来没有做过IEquatable,这可能是我的一个错误,但我认为重写Equals(obj)已经足够好了,我可以根据需要在那里(或在私有助手方法中)进行类型检查。老实说,我不认为IEquatable有什么意义,除非你只是想明确表示,与另一种类型是相等的

IComparable用于排序/排序类型的操作,如果排序中存在一些无法通过重写GetHashCode捕获的业务逻辑,则IComparable非常有用。同样,大多数情况下,我要么使用GetHashCode进行默认排序,要么使用linq OrderBy(),所以这里没有太多的意义


我总是覆盖==/!=当我重写等于时。它使行为更加一致。

正如Ed所说,如果您通过NodeID比较相等,并且NodeID不是一个值类型,那么NodeID类也必须实现
IEquatable
(仅因为您的语法
\u id.Equals(node.id)

也就是说,如果您在接口级别声明
接口INode:IEquatable
,您仍然需要在实例级别的实际实现:例如,对于节点:
公共bool Equals(INode other)

  • 只要您仅在原始接口中声明的属性上进行比较,此方法就有效。如果您有一个具有新属性的派生类,并且您还想使用它来比较相等性(例如NodeID和NodeDate),则接口签名将不会为您的Equals方法提供访问该属性所需的权限
  • 从调用方的角度来看,(希望将一个节点与另一个节点进行比较的API使用者)调用方必须将所有实例强制转换为接口。这可能是最初的意图,只要所有派生类不引入调用方需要但未在接口中声明的内容
  • 如果您发现您正在使用其他派生类属性,或者您的使用者不太可能强制转换到
    INode
    ,那么只需从派生类(节点)扩展
    IEquatable
  • Joe Albhari在他的文章中对这两个接口都有非常可理解和详细的解释

    使用
    IEquatable
    的最重要原因是向类的使用者声明,您保证非默认的相等实现。从框架的角度来看,您声明
    IEquatable
    ,以便使用哈希算法的框架类可以根据您的相等和相等实现进行比较GetHashCode。例如,HashSet集合,以及LINQ扩展,如“Distinct”。即:

  • 如果不同时覆盖
    Equals()
    GetHashCode()
    ,哈希算法将无法正常工作

  • 如果覆盖
    等于(对象)
    GetHashCode()
    ,则不需要声明
    IEquatable

  • 或者,如果您确实声明了它,那么如果您实现了
    Equals(T)
    ,则可以省略
    Equals(object)

  • 您还实现了
    IEquatable
    IComparable
    /
    IComparable
    操作符==
    /
    操作符!=
    中的哪一个?为什么

    我为您描述的环境实现了
    IEquatable
    。因为字典将使用此接口,所以它可以在一个麻木的环境中使用
    public override bool Equals(object obj)
    {
        return Equals(obj as Node);
    }
    
    public bool Equals(Node node)
    {
        if (node == null)
            return false;
        // ... do the type-specific comparison.
    }