【问题标题】:Generic Method to get Distinct of LIST<T>获取 LIST<T> 不同的通用方法
【发布时间】:2016-10-26 14:46:21
【问题描述】:

我正在尝试比较(属性的值)列表中的类型实例并消除重复项。 根据 MSDN,GetHashCode() 是比较两个对象的方法之一。

哈希码用于高效插入和查找 基于哈希表的集合。哈希码不是 永久价值

考虑到这一点,我开始编写我的扩展方法,如下所示

 public static class Linq 
    {
  public static IEnumerable<T> DistinctObjects<T>(this IEnumerable<T> source)
        {
            List<T> newList = new List<T>();
            foreach (var item in source)
            {
                if(newList.All(x => x.GetHashCode() != item.GetHashCode()))
                    newList.Add(item);
            }
            return newList;
        }
}

尽管对象的数据相同,但这个条件总是给我false

newList.All(x => x.GetHashCode() != item.GetHashCode())

最后我想像这样使用它

MyDuplicateList.DistinctObjects().ToList();

如果比较对象的所有字段太多,我可以这样使用,

 MyDuplicateList.DistinctObjects(x=>x.Id, x.Name).ToList();

这里我说的是只比较这些对象的这两个字段。

【问题讨论】:

  • 有一个内置的 LINQ 函数,Distinct(),它接受一个表达式来确定唯一性。这不是你想要的吗?
  • 首先 - 如果 GetHashCode 相等,并不意味着对象相等,反之亦然
  • @GEEF .Distinct() 没有做这项工作。
  • @YacoubMassad GetHashCode() 绝对是由任何 T 实现的,考虑到它基于 C# object
  • 它没有做这项工作,因为您需要使用比较器或为对象实现接口 IEquatable,覆盖 equals 和 gethashcode 否则它正在比较引用

标签: c# generic-collections


【解决方案1】:

阅读您的 cmets 后,我会提出这个解决方案:

   public static IEnumerable<TSource> DistinctBy<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)
    {
        HashSet<TResult> set = new HashSet<TResult>();

        foreach(var item in source)
        {
            var selectedValue = selector(item);

            if (set.Add(selectedValue))
                yield return item;
        }
    }

然后你可以这样使用它:

var distinctedList = myList.DistinctBy(x => x.A);

或者对于这样的多个属性:

var distinctedList = myList.DistinctBy(x => new {x.A,x.B});

此解决方案的优点是您可以准确指定应区分使用的属性,并且不必为每个对象覆盖EqualsGetHashCode。您需要确保可以比较您的属性。

【讨论】:

  • 等一下,这是否只返回我传入 Distinct(x=>x.Id) 的对象?
  • @HaBo 不,返回类型是 IEnumerable。请在 MSDN 上阅读。
  • 这样做的好方法!最好的办法仍然是在每个对象中实现一个适当的 equals / gethashcode,因为默认情况下没有这些你可能会遇到很多问题
【解决方案2】:

您不需要为此创建自己的自定义通用方法。相反,请为您的数据类型提供自定义 EqualityComparar

var myDuplicates = myList.Distinct(new MyComparer());

您可以像这样定义自定义比较器:

public class MyComparer : IEqualityComparer<Mine>
{
    public bool Equals(Mine x, Mine y)
    {
        if (x == null && y == null) return true;            
        if (x == null || y == null) return false;
        return x.Name == y.Name && x.Id == y.Id;
    }

    public int GetHashCode(Mine obj)
    {
        return obj.Name.GetHashCode() ^ obj.Id.GetHashCode();
    }
}

编辑:我最初在这里的代码不正确,这应该可以满足您的要求,而无需覆盖 Equals 运算符

【讨论】:

  • 如果您要展示 Equals / GetHashCode 实现,至少要确保正确执行,Equals 可能会因空异常而崩溃,并且您会忘记 GetHashCode 中的素数
  • 质数不是必需来正确执行此功能。但肯定它肯定会从 NRE 中崩溃,不过他可以填写一些细节。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-07-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多