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
似乎只通过运行时类型检查得到使用(主要在BCL中),从IEquatable
扩展是否有意义INode
- 对于那些您确实实现的,您是只在类本身上实现它们,还是另外在ID上实现它们?
- 例如,
是否也是IEquatable
节点的一部分
- 您是否在
中测试作为节点ID的Equals
对象
- 例如,
(请注意,我们的.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.
}