【问题标题】:Compare list of child to list of parent by specific value按特定值比较子列表与父列表
【发布时间】:2016-03-20 14:56:43
【问题描述】:

我有 2 个列表,第一个是父对象,第二个是子对象。子对象有额外的属性,我想与父类的属性进行比较。

这里是例子

  public class Parent
{
    public int X { get; set; }
}

public class Child : Parent
{
    public int Y { get; set; }
}

public class ClassXCompare : IEqualityComparer<Parent>
{
    public bool Equals(Parent x, Parent y)
    {
        var child = (Child)y;
        return x.X == child.Y;
    }

    public int GetHashCode(Parent parent)
    {
        int parentXhash = parent.X.GetHashCode();
        // Calculate the hash code for the product. 
        return parentXhash ;
    }
}

现在如果我测试以下内容,它总是失败

var parentList= new List<Parent>
        {
            new Parent {X = 5},
            new Parent {X = 6}
        };
        var childList= new List<Child>
        {
            new Child {Y = 5},
            new Child {Y = 6}
        };
        var compare = new ClassXCompare();
        var diff = parentList.Except(childList, compare);
        Assert.IsTrue(!diff.Any()); // Fail ???

我认为我的问题在于 GetHashCode 函数

知道如何解决这个问题吗?

(请忽略应用程序的设计,这是简化的 问题的版本)

【问题讨论】:

  • 您确定yvar child = (Child)y; 中始终是Child 吗?无论如何,请澄清“它总是失败”的确切含义(例外,错误的结果......)。
  • 是的 y 总是一个孩子,没有例外,var diff = parentList.Except(childList, compare); 应该返回空但它没有

标签: c# .net-4.0


【解决方案1】:

这真是可怕的设计。需要在比较器中强制转换为特定类型会给您带来无穷无尽的麻烦。

但是,下面的代码通过了。请注意不同的转换方法、null 检查和 except 行中列表的顺序。

问题在于 Child 实例没有设置 X,并且 except 方法将值传递给 Equals 的顺序意味着“x”是 Child 而不是“y”。

这可能“有效”,但您应该认真重新考虑您的设计。

public class Parent
{
    public int X { get; set; }
}

public class Child : Parent
{
    public int Y { get; set; }
}

public class ClassXCompare : IEqualityComparer<Parent>
{
    public bool Equals(Parent x, Parent y)
    {
        var child = y as Child;

        return child != null && x.X == child.Y;
    }

    public int GetHashCode(Parent parent)
    {
        var c = parent as Child;
        if (c == null)
            return parent.X.GetHashCode();
        else
            return c.Y.GetHashCode();
    }
}

[TestClass]
public class UnitTest1
{
    [TestMethod]
    public void TestMethod1()
    {
        var parentList = new List<Parent>
            {
                    new Parent {X = 5},
                    new Parent {X = 6}
            };
        var childList = new List<Child>
            {
                    new Child {Y = 5},
                    new Child {Y = 6}
            };
        var compare = new ClassXCompare();
        var diff = childList.Except(parentList, compare);
        Assert.IsTrue(!diff.Any()); // Fail ???     
    }
}

【讨论】:

  • 谢谢,我同意这个设计,这就是为什么我在我的问题中指出,问题是我无权更改设计。这就是为什么我必须实现这个:-(再次感谢我会做测试
  • 很公平,不总是这样吗:)
  • 谢谢安迪,我标记了你的答案,我会重新问这个问题,因为在类似的情况下我比较了 2 个双打但我总是得到错误,但这是另一个问题
  • 双精度(或任何 fp 类型)由于所需的精度可能很困难。查看msdn.microsoft.com/en-us/library/ya2zha7s(v=vs.110).aspx 上的示例,了解一些“具有容差的平等”的技术
【解决方案2】:

只有当 GetHashCode 为两个项目返回相同的值时,才会调用 Set/Dictionary 中的 Equals - 请参阅 General advice and guidelines on how to properly override object.GetHashCode()。修复它的最简单方法是使用类似这样的方法,其中子值用于计算 HashCode(如果可用):

public class ClassXCompare : IEqualityComparer<Parent>
{
    public bool Equals(Parent x, Parent y)
    {
        var child = (Child)y;
        return x.X == child.Y;
    }

    public int GetHashCode(Parent parent)
    {
        var child = parent as Child;
        return child == null ?
            parent.X : child.Y.
    }
}

虽然如果你必须处理多个子类,它的可扩展性不是很好。

不确定是否有一种简单的方法可以处理多个子类,除了一些自定义的generic IEqualityComparer,即使这样只会缩短一点。

P.S. 另外,我不确定第二个参数是否始终是 Child(怀疑该标准是否涵盖了它,尽管当前在此特定用法中的实现似乎以适当的方式工作),所以你可能不得不让IEqualityComparer 对参数的顺序更宽松。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-12-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-22
    • 1970-01-01
    相关资源
    最近更新 更多