如何根据基类比较两个对象?
我希望能够比较从 C# 中同一抽象类派生的两个类。下面的代码说明了我的问题。
我现在可以通过将 BaseClass
设置为非抽象来修复代码,然后在 ToBassClass()
中返回一个 new BaseClass
对象。但难道就没有更优雅、更高效的解决方案吗?
abstract class BaseClass
{
BaseClass(int x)
{
X = x;
}
int X { get; private set; }
// It is probably not necessary to override Equals for such a simple class,
// but I've done it to illustrate my point.
override Equals(object other)
{
if (!other is BaseClass)
{
return false;
}
BaseClass otherBaseClass = (BaseClass)other;
return (otherBaseClass.X == this.X);
}
BaseClass ToBaseClass()
{
// The explicit is only included for clarity.
return (BaseClass)this;
}
}
class ClassA : BaseClass
{
ClassA(int x, int y)
: base (x)
{
Y = y;
}
int Y { get; private set; }
}
class ClassB : BaseClass
{
ClassB(int x, int z)
: base (x)
{
Z = z;
}
int Z { get; private set; }
}
var a = new A(1, 2);
var b = new B(1, 3);
// This fails because despite the call to ToBaseClass(), a and b are treated
// as ClassA and ClassB classes so the overridden Equals() is never called.
Assert.AreEqual(a.ToBaseClass(), b.ToBaseClass());
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
这取决于您想要测试相等性的具体位置。显然,
ClassA
和ClassB
实例在该词的真正意义上永远不会“相等”,因此重写Equals
的行为实际上可能会导致您的代码中存在一些奇怪的错误。但是,如果您想根据特定标准对它们进行比较,那么您可以实现特定的IEqualityComparer(或多个比较器)适合您的需求。
因此,在这种情况下,您将拥有:
[编辑] 关于注释:
请注意,这与重写
Equals
方法没有任何关系。但是您将能够像这样检查相等性:
这乍一看可能不是一件好事,但它给您带来了几个好处:
a)您可以拥有尽可能多的
IEqualityComparer ;
实现如你所愿。根据具体情况,您的Equals
覆盖可能并不是那么好。然后,您将面临破坏所有代码的风险。b) 实际上有很多类使用
IEqualityComparer
来比较项目。例如,您可能希望使用
BaseClass
作为字典中的键。在这种情况下,您可以使用Dictionary< /code>
构造函数重载,接受
IEqualityComparer
:这样,字典将在键查找期间使用自定义
ComparerByX
。另外,例如,如果您使用 LINQ,则可以检查 Distinct() 方法示例。它还支持返回不同值的重载,但使用指定的自定义
IEqualityComparer
进行比较。It depends on where exactly you want to test for equality. Clearly
ClassA
andClassB
instances will never be "equal" in the real sense of that word, so overridingEquals
to behave like this might actually cause some weird bugs in your code.But if you want to compare them based on a specific criteria, then you can implement a specific IEqualityComparer (or several comparers) which suites your needs.
So, in this case you would have:
[Edit] Regarding comment:
Note that this doesn't have anything with overriding the
Equals
method.But you will be able to check for equality like this:
This may not seem like a great thing at first, but it gives you several advantages:
a) You can have as many
IEqualityComparer<T>
implementations as you want. Depending on the case, it may turn up that youEquals
override is not so great after all. Then you risk breaking all of your code depending on this.b) There are actually many classes which use
IEqualityComparer<T>
to compare items.For example, you might want to use the
BaseClass
as a key in a dictionary. In that case, you would use theDictionary<Key,Value>
constructor overload which accepts anIEqualityComparer<T>
:This way, dictionary will use the custom
ComparerByX
during key lookup.Also, for example, if you are using LINQ, you can check the Distinct() method example. It also supports an overload which returns distinct values, but compared using the specified custom
IEqualityComparer
.好吧,正如 Freed 指出的那样,在这里使用
Assert.True
有点奇怪 - 您的意思是Assert.AreEqual
吗?如果是这样,我预计它会起作用(即使没有ToBaseClass
调用),尽管它取决于测试框架。然而,在继承方面,平等是很棘手的。就个人而言,我会创建一个适当的
IEqualityComparer
,它明确表示“我将测试对象的这个特定方面” - 这意味着继承基本上不涉及。Well, as Freed pointed out it's a bit odd to use
Assert.True
here - did you meanAssert.AreEqual
? If so, I'd have expected this to work (even without theToBaseClass
call), although it would depend on the test framework.Equality is tricky when it comes to inheritance though. Personally, I'd create an appropriate
IEqualityComparer<BaseClass>
which explicitly says "I'm going to test this specific aspect of an object" - which means that inheritance basically doesn't get involved.首先,您的代码无法编译。其次,当您的代码被修复以便能够编译时(特别是,将
Assert.True
更改为Assert.AreEqual
),我会看到您期望的结果。这是一件好事,因为这是正确的行为。但是您不能依赖继承者不重写Object.Equals
,因此如果您希望仅通过基类进行比较,那么您应该实现IEqualityComparer
。这是您可能想要的代码版本,以便它可以编译:
First, your code doesn't compile. Second, when your code is fixed so that it does compile (in particular,
Assert.True
is changed toAssert.AreEqual
), I see the results that you are expecting. And that's a good thing as that's the correct behavior. But you can't rely on inheritors not overridingObject.Equals
so if you want comparison to go by the base class only then you should implementIEqualityComparer<BaseClass>
.Here's a version of your code as you probably intended it so that it does compile:
我强烈推荐使用 KellermanSoftware.CompareNetObjects(尽管我不是作者 - 它非常灵活、有效,而且到目前为止没有错误!)。
我做了同样的事情来比较基于基类的两个对象。
1) 创建一个“BaseClassComparer”,它采用您想要用于比较的类型:
然后,将此类添加到 Kellerman 比较器:
并进行比较。仅检查/报告基本类型(compareType)之间的差异...
I seriously recommend using KellermanSoftware.CompareNetObjects (although I am not the author - it is very flexible, effective and so far, bug free!).
I did the same thing to compare 2 objects based on a base class.
1) Create a "BaseClassComparer" which takes the type you want to use for comparisons:
Then, add this class to the Kellerman comparer:
And compare away. Only differences between the base type (compareType) will be checked/reported...