【问题标题】:Linq distinct doesn't call Equals methodLinq distinct 不调用 Equals 方法
【发布时间】:2015-04-06 11:11:14
【问题描述】:

我有以下课程

public class ModInfo : IEquatable<ModInfo>
{
    public int ID { get; set; }
    public string MD5 { get; set; }

    public bool Equals(ModInfo other)
    {
        return other.MD5.Equals(MD5);
    }

    public override int GetHashCode()
    {
        return MD5.GetHashCode();
    }
}

我使用这样的方法将一些数据加载到该类的列表中:

public void ReloadEverything() {
    var beforeSort = new List<ModInfo>();
    // Bunch of loading from local sqlite database. 
    // not included since it's reload boring to look at
    var modinfo = beforeSort.OrderBy(m => m.ID).AsEnumerable().Distinct().ToList();
}

问题是Distinct() 调用似乎没有完成它的工作。仍然有彼此相等的对象。

根据这篇文章:https://msdn.microsoft.com/en-us/library/vstudio/bb348436%28v=vs.100%29.aspx 这就是你应该如何做不同的工作,但它似乎没有调用 ModInfo 对象上的 Equals 方法。 什么可能导致这种情况发生?

示例值:

modinfo[0]: id=2069, MD5 =0AAEBF5D2937BDF78CB65807C0DC047C
modinfo[1]: id=2208, MD5 = 0AAEBF5D2937BDF78CB65807C0DC047C

我不在乎选择哪个值,因为 md5 值是相同的,所以它们很可能是相同的。

【问题讨论】:

  • 尝试返回0而不是返回MD5.GetHashCode();在 GetHashCode()
  • 这段代码对我有用。您能否提供 2 个有问题的值并指出您想要保留的值?
  • modinfo[0]: id=2069, MD5 =0AAEBF5D2937BDF78CB65807C0DC047C modinfo[1]: id=2208, MD5 = 0AAEBF5D2937BDF78CB65807C0DC047C 我不在乎选择了哪些对象,如果 md5 值相同,那么对象的其余部分也是
  • 仍然对我有用。它保留 id = 2069 的那个。
  • @RasmusHansen - 检查你的数据库字符串是否被修剪。我想这就是问题所在。

标签: c# linq


【解决方案1】:

您还需要覆盖Object.Equals,而不仅仅是实现IEquatable

如果您将此添加到您的课程中:

public override bool Equals(object other)
{
    ModInfo mod = other as ModInfo;
    if (mod != null)
        return Equals(mod);
    return false;
}

它应该可以工作。

查看这篇文章了解更多信息:Implementing IEquatable Properly

编辑:好的,这是基于 GetHashCode 最佳实践的稍微不同的实现。

public class ModInfo : IEquatable<ModInfo>
{
    public int ID { get; set; }
    public string MD5 { get; set; }

    public bool Equals(ModInfo other)
    {
        if (other == null) return false;
        return (this.MD5.Equals(other.MD5));
    }

    public override int GetHashCode()
    {
        unchecked
        {
            int hash = 13;
            hash = (hash * 7) + MD5.GetHashCode();
            return hash;
        }
    }

    public override bool Equals(object obj)
    {
        ModInfo other = obj as ModInfo;
        if (other != null)
        {
            return Equals(other);
        }
        else
        {
            return false;
        }
    }
}

你可以验证一下:

ModInfo mod1 = new ModInfo {ID = 1, MD5 = "0AAEBF5D2937BDF78CB65807C0DC047C"};
ModInfo mod2 = new ModInfo {ID = 2, MD5 = "0AAEBF5D2937BDF78CB65807C0DC047C"};

// You should get true here
bool areEqual = mod1.Equals(mod2);

List<ModInfo> mods = new List<ModInfo> {mod1, mod2};

// You should get 1 result here
mods = mods.Distinct().ToList();

What's with those specific numbers in GetHashCode?

【讨论】:

  • 那只会比较引用,不会比较对象的实际值吗?
  • 不,除非ModInfo.Equals(ModInfo other) 的实现这样做。
  • 这个Equals 方法正在调用您的equals 方法,该方法进行实际比较。您可能误解了我的意思:您需要将这个方法添加到您的课程中,而不是用我的方法替换您的方法。
  • 我同意这应该可行,但似乎仍然不是这样。
  • 你的 GetHashCode 实现是什么?
【解决方案2】:

添加

public bool Equals(object other)
    {
        return this.Equals(other as ModInfo)
    }

另请参阅此处有关如何实施平等成员的建议:https://msdn.microsoft.com/en-us/library/ms173147(v=vs.80).aspx

【讨论】:

  • 那只会比较引用,而不是对象的实际值,不是吗?除此之外,我不关心 ID(以及我的对象上的一堆其他值)我只关心 md5 是否相等。
  • 不,它会调用 public bool Equals(ModInfo other) 方法,因为 other 被转换为 ModInfo。还修改以下内容以处理“other”为空的情况: public bool Equals(ModInfo other) { if(other == null){return false;} return other.MD5.Equals(MD5); }
猜你喜欢
  • 1970-01-01
  • 2012-03-08
  • 2018-08-02
  • 2012-08-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多