如何改进这段代码:继承和 IEquatable<>? (注:本文为提问标题,无需回答)

10

这是一个关于我正在尝试做的示例:

public class Foo : IEquatable<Foo>
{
    public bool Equals(Foo other)
    {
        Type type1 = this.GetType();
        Type type2 = other.GetType();

        if (type1 != type2)
            return false;

        if (type1 == typeof(A))
        {
            A a = (A)this;
            A b = (A)other;

            return a.Equals(b);
        }
        else if (type1 == typeof(B))
        {
            B c = (B)this;
            B d = (B)other;

            return c.Equals(d);
        }
        else
        {
            throw new Exception("Something is wrong");
        }
    }
}

public class A : Foo, IEquatable<A>
{
    public int Number1 { get; set; }
    public int Number2 { get; set; }

    public bool Equals(A other)
    {
        return this.Number1 == other.Number1 && this.Number2 == other.Number2;
    }
}

public class B : Foo, IEquatable<B>
{
    public int Number1 { get; set; }
    public int Number2 { get; set; }
    public int Number3 { get; set; }

    public bool Equals(B other)
    {
        return this.Number1 == other.Number1 && this.Number2 == other.Number2 && this.Number3 == other.Number3;
    }
}

但正如您在上面看到的,我必须使用许多条件语句“if”来确定实际类型。问题是我必须使用基类。例如:

A a = new A();
Foo foo = a;

foo.Equals(another);

这些类看起来很奇怪。Foo的目的是什么,既然它没有任何属性?另外,如果B只是在A的基础上再加一个Number,为什么不让B继承自A并添加Number3呢,而不是从Foo继承呢? - alun
@oscar.fimbres:当然可以,但是你能告诉我们为什么你需要(或认为需要)IEquatable<Foo>吗?你似乎希望所有东西都通过Foo.Equals(Foo)方法进行处理。 - Ani
@Oscar - 你尝试下面的答案了吗? - RockWorld
@alun 这只是一个例子.. 我有很多类,它们的基类都是Foo,而我有一个Foo类,所以我需要检查一下是否有相等的元素。 - oscar.fimbres
这可能会感兴趣:https://dev59.com/CXI-5IYBdhLWcg3wZ3nC - alun
显示剩余3条评论
4个回答

6
作为对你问题的直接回答,你似乎是通过始终将实现委托给(具体的)子类的IEquatable<self>实现来实现IEquatable<Foo>。代码大致如下:
// You need to specify what you want when this method is called on a 
// vanilla Foo object. I assume here that Foo is abstract. If not, please
// specify desired behaviour.
public bool Equals(Foo other)
{
    if (other == null || other.GetType() != GetType())
        return false;

    // You can cache this MethodInfo..
    var equalsMethod = typeof(IEquatable<>).MakeGenericType(GetType())
                                           .GetMethod("Equals");

    return (bool)equalsMethod.Invoke(this, new object[] { other });
}

但是,为什么需要始终通过基类的“IEquatable”实现进行等值比较并不清楚。
框架已经拥有虚拟的“Equals”方法,它将导致调度等值调用到适当的方法。此外,“EqualityComparar.Default”(大多数集合类型用于进行等值检查)已经具有智能选择“IEquatable.Equals(self)”或“object.Equals(object)”的功能。
试图创建一个在基类中只转发请求的相等实现对任何事情都没有增加价值,就我所看到的而言。
在没有进一步解释为什么需要基类“IEquatable<>”实现的情况下,我建议在每种类型上正确地实现相等。例如:
public class A : Foo, IEquatable<A>
{
    public int Number1 { get; set; }
    public int Number2 { get; set; }

    public bool Equals(A other)
    {
        return other != null 
            && Number1 == other.Number1
            && Number2 == other.Number2;
    }

    public override bool Equals(object obj)
    {
        return Equals(obj as A);
    }

    public override int GetHashCode()
    {
        return Number1 ^ Number2;
    }
}

我认为你在非泛型Equals中漏掉了对泛型Equals的调用。 - alun

1

尝试这段代码:

public class Foo : IEquatable<Foo>
{
    public virtual bool Equals(Foo other)
    {
        return true;
    }
}

public class A : Foo,IEquatable<A>
{
    public int Number1 { get; set; }
    public int Number2 { get; set; }

    public override bool Equals(Foo other)
    {
        if (other.GetType() == typeof(A))
        {
            return Equals((A)other);                
        }
        throw new InvalidOperationException("Object is not of type A");
    }
    public bool Equals(A other)
    {
        return this.Number1 == other.Number1 && this.Number2 == other.Number2;
    }
}

public class B : Foo,IEquatable<B>
{
    public int Number1 { get; set; }
    public int Number2 { get; set; }
    public int Number3 { get; set; }

    public override bool Equals(Foo other)
    {
        if (other.GetType() == typeof(B))
        {
            return Equals((B)other);

        }
        throw new InvalidOperationException("Object is not of type B");
    }
    public bool Equals(B other)
    {
        return this.Number1 == other.Number1 && this.Number2 == other.Number2 && this.Number3 == other.Number3;
    }
}

注意:您可以使用 Assert 功能进行类型检查。

看起来不错,一开始我想将Foo类建立为抽象类。我喜欢这种方式,虽然在Foo类中使用virtual看起来有些奇怪,但是可以将Foo类替换为接口吗? - oscar.fimbres

0
一种选择是将Number1和Number2属性移动到基类中,在子类的相等方法中仅比较添加到子类中的成员。
class Foo
{
    // move the common properties to the base class
    public int Number1 { get; set; }
    public int Number2 { get; set; }

    public override bool Equals(object obj)
    {
        Foo objfoo = obj as Foo;
        return 
            objfoo != null
            // require objects being compared to be of
            // the same derived type (optionally)
            && this.GetType() == obj.GetType()
            && objfoo.Number1 == this.Number1
            && objfoo.Number2 == this.Number2;
    }
    public override int GetHashCode()
    {
        // xor the hash codes of the elements used to evaluate
        // equality
        return Number1.GetHashCode() ^ Number2.GetHashCode();
    }
}

class A : Foo, IEquatable<A>
{
    // A has no properties Foo does not.  Simply implement
    // IEquatable<A>

    public bool Equals(A other)
    {
        return this.Equals(other);
    }

    // can optionally override Equals(object) and GetHashCode()
    // to call base methods here
}

class B : Foo, IEquatable<B>
{
    // Add property Number3 to B
    public int Number3 { get; set; }
    public bool Equals(B other)
    {
        // base.Equals(other) evaluates Number1 and Number2
        return base.Equals(other)
            && this.Number3 == other.Number3;
    }
    public override int GetHashCode()
    {
        // include Number3 in the hashcode, since it is used
        // to evaluate equality
        return base.GetHashCode() ^ Number3.GetHashCode();
    }
    public override bool Equals(object obj)
    {
        return this.Equals(obj as B);
    }
}

0

我认为基类不应该处理派生类。通常,“Foo”对A和B一无所知。

仍然可以将基本的IEquatable实现设置为虚拟的,允许A和B覆盖它并执行它们特定的相等性检查,即使相等性检查和检查的实例仅作为“Foo”或“Object”提供。

这将把.Equals(Foo obj)视为Object.Equals(Object obj)的更具体形式。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接