如何根据基类比较两个对象?

发布于 2024-08-09 14:56:58 字数 1285 浏览 3 评论 0 原文

我希望能够比较从 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());

I would like to be able to compare two classes derived from the same abstract class in C#. The following code illustrates my problem.

I now I could fix the code by making BaseClass non abstract and then return a new BaseClass object in ToBassClass(). But isn't there a more elegant and efficient solution?

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

轻拂→两袖风尘 2024-08-16 14:56:58

这取决于您想要测试相等性的具体位置。显然,ClassAClassB 实例在该词的真正意义上永远不会“相等”,因此重写 Equals 的行为实际上可能会导致您的代码中存在一些奇怪的错误。

但是,如果您想根据特定标准对它们进行比较,那么您可以实现特定的IEqualityComparer(或多个比较器)适合您的需求。

因此,在这种情况下,您将拥有:

/// <Summary>
/// Compares two classes based only on the value of their X property.
/// </Summary>
public class ComparerByX : IEqualityComparer<BaseClass>
{
     #region IEqualityComparer<BaseClass> Members

     public bool Equals(BaseClass a, BaseClass b)
     {
         return (a.X == b.X);
     }

     public int GetHashCode(BaseClass obj)
     {
         return obj.X.GetHashCode();
     }

     #endregion

}

[编辑] 关于注释:

请注意,这与重写 Equals 方法没有任何关系。

但是您将能够像这样检查相等性:

IEqualityComparer<BaseClass> comparer = new ComparerByX();
Assert.True(comparer.Equals(a, b));

这乍一看可能不是一件好事,但它给您带来了几个好处:

a)您可以拥有尽可能多的IEqualityComparer ; 实现如你所愿。根据具体情况,您的 Equals 覆盖可能并不是那么好。然后,您将面临破坏所有代码的风险。

b) 实际上有很多类使用 IEqualityComparer 来比较项目。

例如,您可能希望使用 BaseClass 作为字典中的键。在这种情况下,您可以使用 Dictionary< /code> 构造函数重载,接受 IEqualityComparer

Dictionary<BaseClass, SomeOtherClass> dictionary 
    = new Dictionary<BaseClass, SomeOtherClass>(new ComparerByX());

这样,字典将在键查找期间使用自定义 ComparerByX

另外,例如,如果您使用 LINQ,则可以检查 Distinct() 方法示例。它还支持返回不同值的重载,但使用指定的自定义 IEqualityComparer 进行比较。

It depends on where exactly you want to test for equality. Clearly ClassA and ClassB instances will never be "equal" in the real sense of that word, so overriding Equals 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:

/// <Summary>
/// Compares two classes based only on the value of their X property.
/// </Summary>
public class ComparerByX : IEqualityComparer<BaseClass>
{
     #region IEqualityComparer<BaseClass> Members

     public bool Equals(BaseClass a, BaseClass b)
     {
         return (a.X == b.X);
     }

     public int GetHashCode(BaseClass obj)
     {
         return obj.X.GetHashCode();
     }

     #endregion

}

[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:

IEqualityComparer<BaseClass> comparer = new ComparerByX();
Assert.True(comparer.Equals(a, b));

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 you Equals 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 the Dictionary<Key,Value> constructor overload which accepts an IEqualityComparer<T>:

Dictionary<BaseClass, SomeOtherClass> dictionary 
    = new Dictionary<BaseClass, SomeOtherClass>(new ComparerByX());

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.

你是我的挚爱i 2024-08-16 14:56:58

好吧,正如 Freed 指出的那样,在这里使用 Assert.True 有点奇怪 - 您的意思是 Assert.AreEqual 吗?如果是这样,我预计它会起作用(即使没有 ToBaseClass 调用),尽管它取决于测试框架。

然而,在继承方面,平等是很棘手的。就个人而言,我会创建一个适当的 IEqualityComparer ,它明确表示“我将测试对象的这个特定方面” - 这意味着继承基本上不涉及。

Well, as Freed pointed out it's a bit odd to use Assert.True here - did you mean Assert.AreEqual? If so, I'd have expected this to work (even without the ToBaseClass 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.

甜点 2024-08-16 14:56:58

首先,您的代码无法编译。其次,当您的代码被修复以便能够编译时(特别是,将 Assert.True 更改为 Assert.AreEqual),我会看到您期望的结果。这是一件好事,因为这是正确的行为。但是您不能依赖继承者不重写Object.Equals,因此如果您希望仅通过基类进行比较,那么您应该实现IEqualityComparer

这是您可能想要的代码版本,以便它可以编译:

abstract class BaseClass {
    public BaseClass(int x) { X = x; }

    public int X { get; private set; }

    public override bool  Equals(object other) {
        if (!(other is BaseClass)) {
            return false; 
        }

        BaseClass otherBaseClass = (BaseClass)other;
        return (otherBaseClass.X == this.X);
    }

    public BaseClass ToBaseClass() {
        return (BaseClass)this;
    }
}

class ClassA : BaseClass {
    public ClassA(int x, int y) : base (x) {
        Y = y;
    }

    public int Y { get; private set; }
}

class ClassB : BaseClass {
    public ClassB(int x, int z) : base (x) {
        Z = z;
    }

    public int Z { get; private set; }
}

class Program {
    static void Main(string[] args) {
        var a = new ClassA(1, 2);
        var b = new ClassB(1, 3);
        Assert.AreEqual(a.ToBaseClass(), b.ToBaseClass());
    }
}

First, your code doesn't compile. Second, when your code is fixed so that it does compile (in particular, Assert.True is changed to Assert.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 overriding Object.Equals so if you want comparison to go by the base class only then you should implement IEqualityComparer<BaseClass>.

Here's a version of your code as you probably intended it so that it does compile:

abstract class BaseClass {
    public BaseClass(int x) { X = x; }

    public int X { get; private set; }

    public override bool  Equals(object other) {
        if (!(other is BaseClass)) {
            return false; 
        }

        BaseClass otherBaseClass = (BaseClass)other;
        return (otherBaseClass.X == this.X);
    }

    public BaseClass ToBaseClass() {
        return (BaseClass)this;
    }
}

class ClassA : BaseClass {
    public ClassA(int x, int y) : base (x) {
        Y = y;
    }

    public int Y { get; private set; }
}

class ClassB : BaseClass {
    public ClassB(int x, int z) : base (x) {
        Z = z;
    }

    public int Z { get; private set; }
}

class Program {
    static void Main(string[] args) {
        var a = new ClassA(1, 2);
        var b = new ClassB(1, 3);
        Assert.AreEqual(a.ToBaseClass(), b.ToBaseClass());
    }
}
爱你是孤单的心事 2024-08-16 14:56:58

我强烈推荐使用 KellermanSoftware.CompareNetObjects(尽管我不是作者 - 它非常灵活、有效,而且到目前为止没有错误!)。

我做了同样的事情来比较基于基类的两个对象。

1) 创建一个“BaseClassComparer”,它采用您想要用于比较的类型:

using System;
using KellermanSoftware.CompareNetObjects;

namespace Compare
{

    /// <summary>
    /// This allows us to compare objects based on a particular class (ie so we can compare on base classes)
    /// </summary>
    public class BaseClassComparer : KellermanSoftware.CompareNetObjects.TypeComparers.ClassComparer
    {


        private readonly Type _compareType;
        internal BaseClassComparer(Type compareType, RootComparer rootComparer) : base(rootComparer)
        {

            _compareType = compareType;
        }

        public override void CompareType(CompareParms parms)
        {
            parms.Object1Type = _compareType;
            parms.Object2Type = _compareType;

            base.CompareType(parms);
        }

        public override bool IsTypeMatch(Type type1, Type type2)
        {
            if (((_compareType.IsAssignableFrom(type1)) && (_compareType.IsAssignableFrom(type2)))) {
                return true;
            } else {
                return false;
            }
        }
    }
}

然后,将此类添加到 Kellerman 比较器:

_compare = New CompareLogic
_compare.Config.CustomComparers.Add(New BaseClassComparer(compareType, RootComparerFactory.GetRootComparer()))

并进行比较。仅检查/报告基本类型(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:

using System;
using KellermanSoftware.CompareNetObjects;

namespace Compare
{

    /// <summary>
    /// This allows us to compare objects based on a particular class (ie so we can compare on base classes)
    /// </summary>
    public class BaseClassComparer : KellermanSoftware.CompareNetObjects.TypeComparers.ClassComparer
    {


        private readonly Type _compareType;
        internal BaseClassComparer(Type compareType, RootComparer rootComparer) : base(rootComparer)
        {

            _compareType = compareType;
        }

        public override void CompareType(CompareParms parms)
        {
            parms.Object1Type = _compareType;
            parms.Object2Type = _compareType;

            base.CompareType(parms);
        }

        public override bool IsTypeMatch(Type type1, Type type2)
        {
            if (((_compareType.IsAssignableFrom(type1)) && (_compareType.IsAssignableFrom(type2)))) {
                return true;
            } else {
                return false;
            }
        }
    }
}

Then, add this class to the Kellerman comparer:

_compare = New CompareLogic
_compare.Config.CustomComparers.Add(New BaseClassComparer(compareType, RootComparerFactory.GetRootComparer()))

And compare away. Only differences between the base type (compareType) will be checked/reported...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文