正确实现两个类型不同但语义等价的对象的比较
本文关键字:语义 比较 对象 类型 实现 两个 | 更新日期: 2023-09-27 18:02:51
我发现了一个类似的问题
如何比较具有相似属性的两个截然不同的对象
可以含蓄地和/或部分地回答我的问题。
假设我想比较(没有很多嵌套条件)这个对象:
class ObjectA {
public string PropertyX { get; set; }
public char PropertyY { get; set; }
public long PropertyZ { get; set; }
}
到a System.String
。我只对等式或不等式(不是关于恒等的值范围)感兴趣。
在ObjectA
中实施IEquatable<string>
是正确的选择?我不关心什么简单地起作用,我想确定这种情况下的正确模式。
作为其他信息,请考虑ObjectA
将经常作为IEnumerable<ObjectA>
的序列提供。
我不需要知道"string"
==
或!=
objectA
instance;不涉及排序。
编辑以澄清(和帮助)
对不起,写一个好问题有时很难…
假设我不能将ObjectA
表示为字符串用于比较(违反封装不是一个选项)。
在context-1中,我必须将其与
PropertyY
相匹配。在context-2中,我必须将其与应用于
PropertyY
/PropertyZ
的算法相匹配。
@Oliver问题最后的解决方案再次帮助了我(并且再次+1)。我可以简单地定义一个自定义接口:
interface IContextConverter {
string ToEquatableStringForContext1();
string ToEquatableStringForContext2();
}
因为我也有一个具有相同逻辑但不同属性的ObjectB
,两者都将实现IContextConverter
(或者我可能找到一个更好的名字)避免违反RAP。
我强烈建议不要实现IEquatable<string>
,因为特别是在使用集合、字典、LINQ等时,你真的不知道这些方法中的一个何时会在内部深处被调用,这可能会导致微妙的bug。
由于你喜欢比较两个不同类型的对象,一个简单的Comparer<T>
也不能工作。
所以要么写一个TypeConverter
,将你的对象转换成所需的类型(在你的情况下是string
),要么为你的对象添加一个方法,如.ToEquatableString()
,并使用它们的输出来比较你的对象与其他字符串。
这里有一个例子,你可以得到所有的元素,匹配字符串中的一个在另一个集合:
IEnumerable<String> otherElements = new[] {"abc", "def", "ghi" };
IEnumerable<ObjectA> myObjects = GetObjects();
var matchesFound = otherElements.Join( // Take the first collection.
myObjects, // Take the second collection.
s => s, // Use the elements in the first collection as key (the string).
obj => obj.ToEquatableString(), // Create a string from each object for comparison.
(s, obj) => obj, // From the matching pairs take simply the objects found.
StringComparer.OrdinalIgnoreCase); // Use a special string comparer if desired.
有很多可能性。
如果您认为ObjectA
是System.String
的一种,您可以编写自定义的转换(隐式或显式),从ObjectA
到System.String
,或从System.String
到ObjectA
,或两个方向。
还可以用类似operator ==(ObjectA oa, string s)
的签名重载==
和!=
操作符。注意oa == s
和s == oa
之间存在差异。
这两种可能性中的任何一种都可能导致混淆。覆盖虚拟Equals(object)
或引入重载Equals(string)
也会令人困惑。因此,我不建议实现IEquatable<string>
。
为什么不简单地写一个没有使用的名字的方法,比如public bool EqualsString(string s)
?当然,您必须显式地调用这个方法,但这样可以减少混淆。另一个想法是使用签名public ObjectA(string s)
的构造函数,然后实现ObjectA
和ObjectA
的"同构"相等。
注意:对于这种特殊情况,我并不特别推荐这种解决方案,但是我经常使用这个框架来实现结构的值相等。在我们的领域中,困难的权衡是很常见的,解决这些问题的答案,加上适当的警告,似乎是有序的。
我认为你希望在你的类上设计值相等语义,就像。net框架对字符串类所做的那样。至少,以下内容是必要的:
public override bool Equals(object obj) {
return (obj is ObjectA) && this == (ObjectA)obj;
}
bool IEquatable<ObjectA>.Equals(ObjectA rhs) {
return this == rhs;
}
public static bool operator != (ObjectA lhs, ObjectA rhs) {
return ! (lhs == rhs);
}
public static bool operator == (ObjectA lhs, ObjectA rhs) {
return (lhs.PropertyX == rhs.PropertyX);
}
public override int GetHashCode() {
return PropertyX.GetHashCode()
}
扩展以允许ObjectA和string之间的值比较:
bool IEquatable<ObjectA>.Equals(string rhs) {
return this == rhs;
}
public static bool operator != (ObjectA lhs, string rhs) {
return ! (lhs == rhs);
}
public static bool operator != (string lhs, ObjectA rhs) {
return ! (lhs == rhs);
}
public static bool operator == (ObjectA lhs, string rhs) {
return (lhs.PropertyX == rhs);
}
public static bool operator == (string lhs, ObjectA rhs) {
return (lhs == rhs.PropertyX);
}