如何改进此代码:继承和IEquatable<>

时间:2011-08-13 14:56:35

标签: c# wpf

这是我要做的事情的一个例子:

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);

4 个答案:

答案 0 :(得分:4)

作为直接回答您的问题,您似乎通过始终遵循(具体)子类的IEquatable<Foo>实现来实现IEquatable<self>。这看起来像是:

(错误代码,仅供演示)

// 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<self>实现。

框架已经具有虚拟Equals方法,该方法将导致调度相应方法的相等调用。此外,EqualityComparar<T>.Default(大多数集合类型用于进行等式检查)已经可以根据需要选择IEquatable<self>.Equals(self)object.Equals(object)。 / p>

尝试在基类中创建一个只是转发请求的相等实现,就我所见,将 no value 添加到任何内容中。

如果没有对为什么进行进一步解释,您需要基类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;
    }
}

答案 1 :(得分: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功能进行类型检查。

答案 2 :(得分: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);
    }
}

答案 3 :(得分:0)

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

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

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