对LINQ和对象使用Distinct

对LINQ和对象使用Distinct,linq,distinct,Linq,Distinct,直到最近,我还在使用LINQ中的Distinct从表中选择一个不同的类别(枚举)。这很好用 我现在需要在一个包含类别和国家(都是枚举)的类上对其进行区分。分离机现在不工作了 我做错了什么?我相信这篇文章解释了你的问题: 上面链接的内容可以总结为,Distinct()方法可以通过执行以下操作来替换 var distinctItems = items .GroupBy(x => x.PropertyToCompare) .Select(x => x.Firs

直到最近,我还在使用LINQ中的Distinct从表中选择一个不同的类别(枚举)。这很好用

我现在需要在一个包含类别和国家(都是枚举)的类上对其进行区分。分离机现在不工作了


我做错了什么?

我相信这篇文章解释了你的问题:

上面链接的内容可以总结为,Distinct()方法可以通过执行以下操作来替换

var distinctItems = items
       .GroupBy(x => x.PropertyToCompare)
       .Select(x => x.First());

试试液体比较法

public class MyObjEqualityComparer : IEqualityComparer<MyObj>
{
    public bool Equals(MyObj x, MyObj y)
    {
        return x.Category.Equals(y.Category) &&
               x.Country.Equals(y.Country);
    }

    public int GetHashCode(MyObj obj)
    {
        return obj.GetHashCode();
    }
}

有关解释,请看其他答案。我只是提供一种处理这个问题的方法

您可能会喜欢:


注意:只对LINQ2对象可靠地工作

您没有做错,这只是.NET Framework中
.Distinct()
的糟糕实现

修复它的一种方法已在其他答案中显示,但也有一种较短的解决方案可用,其优点是您可以在任何地方轻松地将其用作扩展方法,而无需调整对象的哈希值

看看这个:


**用法:** 注意:此示例使用数据库查询,但它也适用于可枚举对象列表。


MyDistinct的声明:

public static class Extensions
{
    public static IEnumerable<T> MyDistinct<T, V>(this IEnumerable<T> query, 
                                                    Func<T, V> f)
    {
        return query.GroupBy(f).Select(x=>x.First());
    }
}
公共静态类扩展
{
公共静态IEnumerable MyDistinct(此IEnumerable查询,
函数(f)
{
return query.GroupBy(f).Select(x=>x.First());
}
}
或者,如果您希望它更短,则与上面相同,但为“一行”:

公共静态IEnumerable MyDistinct(此IEnumerable查询,Func f)
=>query.GroupBy(f).选择(x=>x.First());

它适用于任何事物,对象和实体。如果需要,您可以为
IQueryable
创建第二个重载扩展方法,只需替换我上面给出的示例中的返回类型和第一个参数类型。

我知道这是一个老问题,但我对任何答案都不满意。我花了一些时间来为自己解决这个问题,我想分享我的发现

首先,阅读和理解这两件事很重要:

  • 长话短说为了使
    .Distinct()
    扩展理解如何确定对象的相等性,您必须为对象T定义一个“相等比较程序”。当您阅读Microsoft文档时,它会逐字说明:

    我们建议您从EqualityComparer类派生 而不是实现IEqualityComparer接口

    这就是你决定使用什么的方式,因为它已经为你决定了

    要使
    .Distinct()
    扩展成功工作,您必须确保可以准确地比较对象。在
    .Distinct()
    的情况下,真正重要的是
    GetHashCode()
    方法

    您可以自己编写一个
    GetHashCode()
    实现来测试这一点,该实现只返回传入对象的当前哈希代码,您将看到结果不好,因为该值在每次运行时都会更改。这使得您的对象过于独特,这就是为什么实际编写此方法的正确实现非常重要的原因

    下面是
    IEqualityComparer
    页面上的代码示例的精确副本,其中包含测试数据、对
    GetHashCode()
    方法的小修改以及用于说明这一点的注释

    //Did this in LinqPad
    void Main()
    {
        var lst = new List<Box>
        {
            new Box(1, 1, 1),
            new Box(1, 1, 1),
            new Box(1, 1, 1),
            new Box(1, 1, 1),
            new Box(1, 1, 1)
        };
    
        //Demonstration that the hash code for each object is fairly 
        //random and won't help you for getting a distinct list
        lst.ForEach(x => Console.WriteLine(x.GetHashCode()));
    
        //Demonstration that if your EqualityComparer is setup correctly
        //then you will get a distinct list
        lst = lst
            .Distinct(new BoxEqualityComparer())
            .ToList();
    
        lst.Dump();
    }
    
    public class Box
    {
        public Box(int h, int l, int w)
        {
            this.Height = h;
            this.Length = l;
            this.Width = w;
        }
    
        public int Height { get; set; }
        public int Length { get; set; }
        public int Width { get; set; }
    
        public override String ToString()
        {
            return String.Format("({0}, {1}, {2})", Height, Length, Width);
        }
    }
    
    public class BoxEqualityComparer 
        : EqualityComparer<Box>
    {
        public override bool Equals(Box b1, Box b2)
        {
            if (b2 == null && b1 == null)
                return true;
            else if (b1 == null || b2 == null)
                return false;
            else if (b1.Height == b2.Height && b1.Length == b2.Length
                                && b1.Width == b2.Width)
                return true;
            else
                return false;
        }
    
        public override int GetHashCode(Box bx)
        {
            #region This works
            //In this example each component of the box object are being XOR'd together
            int hCode = bx.Height ^ bx.Length ^ bx.Width;
    
            //The hashcode of an integer, is that same integer
            return hCode.GetHashCode();
            #endregion
    
            #region This won't work
            //Comment the above lines and uncomment this line below if you want to see Distinct() not work
            //return bx.GetHashCode();
            #endregion
        }
    }
    
    //这是在LinqPad中完成的
    void Main()
    {
    var lst=新列表
    {
    新盒子(1,1,1),
    新盒子(1,1,1),
    新盒子(1,1,1),
    新盒子(1,1,1),
    新盒子(1,1,1)
    };
    //演示每个对象的哈希代码是否正确
    //随机的,不会帮助你得到一个明确的列表
    lst.ForEach(x=>Console.WriteLine(x.GetHashCode());
    //演示EqualityComparer是否正确设置
    //然后您将得到一个不同的列表
    lst=lst
    .Distinct(新的BoxEqualityComparer())
    .ToList();
    lst.Dump();
    }
    公共类箱
    {
    公用箱(内部h、内部l、内部w)
    {
    这个高度=h;
    这个。长度=l;
    这个。宽度=w;
    }
    公共整数高度{get;set;}
    公共整数长度{get;set;}
    公共整数宽度{get;set;}
    公共重写字符串ToString()
    {
    返回String.Format(({0},{1},{2})”,高度、长度、宽度);
    }
    }
    公共类拳击手
    :相等比较
    {
    公共覆盖布尔等于(框b1、框b2)
    {
    如果(b2==null&&b1==null)
    返回true;
    else if(b1==null | | b2==null)
    返回false;
    如果(b1.Height==b2.Height&&b1.Length==b2.Length
    &&b1.宽度==b2.宽度)
    返回true;
    其他的
    返回false;
    }
    公共覆盖int GetHashCode(框bx)
    {
    #这个区域有效
    //在本例中,box对象的每个组件都是异或的
    int hCode=bx.高度^bx.长度^bx.宽度;
    //整数的哈希代码,就是同一个整数
    返回hCode.GetHashCode();
    #端区
    #这是行不通的
    //如果希望看到Distinct()不起作用,请对上面的行进行注释,并取消对下面这行的注释
    //返回bx.GetHashCode();
    #端区
    }
    }
    
    请不要仅通过链接来回答。答案中包含了相关代码。是的,我不再这样做了,但在2010年我没有注意到这种做法。我喜欢这种解决方案,但我有一个问题。GetHashCode导致它找不到匹配项。我不得不将其更改为类似于
    return obj.Category.GetHashCode()+obj.Country.GetHashCode()
    public void Foo{
      public string Fizz{get;set;}
      public BarEnum Bar{get;set;}
    }
    
    public enum BarEnum {One,Two,Three}
    
    var lst=new List<Foo>();
    lst.Distinct(new LambdaComparer<Foo>(
      (x1,x2)=>x1.Fizz==x2.Fizz&&
               x1.Bar==x2.Bar));
    
    public static class EnumerableExtensions{
     public static IEnumerable<T> SmartDistinct<T>
      (this IEnumerable<T> lst, Func<T, T, bool> pred){
       return lst.Distinct(new LambdaComparer<T>(pred));
     }
    }
    
    lst.SmartDistinct((x1,x2)=>x1.Fizz==x2.Fizz&&x1.Bar==x2.Bar);
    
    var myQuery=(from x in Customers select x).MyDistinct(d => d.CustomerID);
    
    public static class Extensions
    {
        public static IEnumerable<T> MyDistinct<T, V>(this IEnumerable<T> query, 
                                                        Func<T, V> f)
        {
            return query.GroupBy(f).Select(x=>x.First());
        }
    }
    
    public static IEnumerable<T> MyDistinct<T, V>(this IEnumerable<T> query, Func<T, V> f) 
                                 => query.GroupBy(f).Select(x => x.First());
    
    //Did this in LinqPad
    void Main()
    {
        var lst = new List<Box>
        {
            new Box(1, 1, 1),
            new Box(1, 1, 1),
            new Box(1, 1, 1),
            new Box(1, 1, 1),
            new Box(1, 1, 1)
        };
    
        //Demonstration that the hash code for each object is fairly 
        //random and won't help you for getting a distinct list
        lst.ForEach(x => Console.WriteLine(x.GetHashCode()));
    
        //Demonstration that if your EqualityComparer is setup correctly
        //then you will get a distinct list
        lst = lst
            .Distinct(new BoxEqualityComparer())
            .ToList();
    
        lst.Dump();
    }
    
    public class Box
    {
        public Box(int h, int l, int w)
        {
            this.Height = h;
            this.Length = l;
            this.Width = w;
        }
    
        public int Height { get; set; }
        public int Length { get; set; }
        public int Width { get; set; }
    
        public override String ToString()
        {
            return String.Format("({0}, {1}, {2})", Height, Length, Width);
        }
    }
    
    public class BoxEqualityComparer 
        : EqualityComparer<Box>
    {
        public override bool Equals(Box b1, Box b2)
        {
            if (b2 == null && b1 == null)
                return true;
            else if (b1 == null || b2 == null)
                return false;
            else if (b1.Height == b2.Height && b1.Length == b2.Length
                                && b1.Width == b2.Width)
                return true;
            else
                return false;
        }
    
        public override int GetHashCode(Box bx)
        {
            #region This works
            //In this example each component of the box object are being XOR'd together
            int hCode = bx.Height ^ bx.Length ^ bx.Width;
    
            //The hashcode of an integer, is that same integer
            return hCode.GetHashCode();
            #endregion
    
            #region This won't work
            //Comment the above lines and uncomment this line below if you want to see Distinct() not work
            //return bx.GetHashCode();
            #endregion
        }
    }