LINQ选择与匿名类型不同

时间:2009-02-12 21:46:57

标签: c# linq distinct anonymous-types equality

所以我有一组对象。确切的类型并不重要。从中我想提取一对特定属性的所有唯一对,因此:

myObjectCollection.Select(item=>new
                                {
                                     Alpha = item.propOne,
                                     Bravo = item.propTwo
                                }
                 ).Distinct();

所以我的问题是:在这种情况下,Will Distinct会使用默认对象equals(这对我来说没用,因为每个对象都是新的)或者可以告诉它做一个不同的equals(在这种情况下,相等的值) Alpha和Bravo =>相等的实例)?有没有办法实现这个结果,如果不这样做的话?

8 个答案:

答案 0 :(得分:175)

请阅读K. Scott Allen的精彩帖子:

And Equality for All ... Anonymous Types

简短的回答(我引用):

  

结果是C#编译器覆盖   Equals和GetHashCode为匿名   类型。两者的实施   重写方法使用所有公共   计算类型的属性   对象的哈希码和测试   平等。如果两个对象相同   匿名类型都是一样的   他们的财产价值 -   对象是平等的。

因此,对返回匿名类型的查询使用Distinct()方法是完全安全的。

答案 1 :(得分:14)

public class DelegateComparer<T> : IEqualityComparer<T>
{
    private Func<T, T, bool> _equals;
    private Func<T, int> _hashCode;
    public DelegateComparer(Func<T, T, bool> equals, Func<T, int> hashCode)
    {
        _equals= equals;
        _hashCode = hashCode;
    }
    public bool Equals(T x, T y)
    {
        return _equals(x, y);
    }

    public int GetHashCode(T obj)
    {
        if(_hashCode!=null)
            return _hashCode(obj);
        return obj.GetHashCode();
    }       
}

public static class Extensions
{
    public static IEnumerable<T> Distinct<T>(this IEnumerable<T> items, 
        Func<T, T, bool> equals, Func<T,int> hashCode)
    {
        return items.Distinct(new DelegateComparer<T>(equals, hashCode));    
    }
    public static IEnumerable<T> Distinct<T>(this IEnumerable<T> items,
        Func<T, T, bool> equals)
    {
        return items.Distinct(new DelegateComparer<T>(equals,null));
    }
}

var uniqueItems=students.Select(s=> new {FirstName=s.FirstName, LastName=s.LastName})
            .Distinct((a,b) => a.FirstName==b.FirstName, c => c.FirstName.GetHashCode()).ToList();

很抱歉早先搞砸了格式化

答案 2 :(得分:5)

有趣的是它在C#中工作但在VB中不起作用

返回26个字母:

var MyBet = "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ";
MyBet.ToCharArray()
.Select(x => new {lower = x.ToString().ToLower(), upper = x.ToString().ToUpper()})
.Distinct()
.Dump();

返回52 ...

Dim MyBet = "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ"
MyBet.ToCharArray() _
.Select(Function(x) New With {.lower = x.ToString.ToLower(), .upper = x.ToString.ToUpper()}) _
.Distinct() _
.Dump()

答案 3 :(得分:3)

我运行了一点测试,发现如果属性是值类型,它似乎工作正常。如果它们不是值类型,那么类型需要为它提供自己的Equals和GetHashCode实现。我认为,弦乐会起作用。

答案 4 :(得分:2)

您可以创建自己的Distinct Extension方法,该方法采用lambda表达式。这是一个例子

创建一个派生自IEqualityComparer接口的类

public class DelegateComparer<T> : IEqualityComparer<T>
{
    private Func<T, T, bool> _equals;
    private Func<T, int> _hashCode;
    public DelegateComparer(Func<T, T, bool> equals, Func<T, int> hashCode)
    {
        _equals= equals;
        _hashCode = hashCode;
    }
    public bool Equals(T x, T y)
    {
        return _equals(x, y);
    }

    public int GetHashCode(T obj)
    {
        if(_hashCode!=null)
            return _hashCode(obj);
        return obj.GetHashCode();
    }       
}

然后创建您的Distinct Extension方法

public static class Extensions
{
    public static IEnumerable<T> Distinct<T>(this IEnumerable<T> items, 
        Func<T, T, bool> equals, Func<T,int> hashCode)
    {
        return items.Distinct(new DelegateComparer<T>(equals, hashCode));    
    }
    public static IEnumerable<T> Distinct<T>(this IEnumerable<T> items,
        Func<T, T, bool> equals)
    {
        return items.Distinct(new DelegateComparer<T>(equals,null));
    }
}

您可以使用此方法查找不同的项目

var uniqueItems=students.Select(s=> new {FirstName=s.FirstName, LastName=s.LastName})
            .Distinct((a,b) => a.FirstName==b.FirstName, c => c.FirstName.GetHashCode()).ToList();

答案 5 :(得分:0)

如果AlphaBravo都从公共类继承,您将能够通过实现IEquatable<T>来指定父类中的相等性检查。

例如:

public class CommonClass : IEquatable<CommonClass>
{
    // needed for Distinct()
    public override int GetHashCode() 
    {
        return base.GetHashCode();
    }

    public bool Equals(CommonClass other)
    {
        if (other == null) return false;
        return [equality test];
    }
}

答案 6 :(得分:0)

嘿,我遇到了同样的问题,我找到了解决方案。 您必须实现IEquatable接口或简单地覆盖 (Equals&amp; GetHashCode)方法。但这不是技巧,GetHashCode方法中的技巧。您不应该返回类对象的哈希码,但是您应该返回要比较的属性的哈希值。

public override bool Equals(object obj)
    {
        Person p = obj as Person;
        if ( obj == null )
            return false;
        if ( object.ReferenceEquals( p , this ) )
            return true;
        if ( p.Age == this.Age && p.Name == this.Name && p.IsEgyptian == this.IsEgyptian )
            return true;
        return false;
        //return base.Equals( obj );
    }
    public override int GetHashCode()
    {
        return Name.GetHashCode();
    }

如你所见,我有一个名为person的类有3个属性(Name,Age,IsEgyptian“因为我是”)在GetHashCode中,我返回了Name属性的哈希,而不是Person对象。

尝试它,它将工作ISA。 谢谢, Modather Sadik

答案 7 :(得分:0)

为使其能够在VB.NET中工作,您需要在匿名类型中的每个属性之前指定Key关键字,如下所示:

myObjectCollection.Select(Function(item) New With
{
    Key .Alpha = item.propOne,
    Key .Bravo = item.propTwo
}).Distinct()

我为此感到挣扎,我以为VB.NET不支持这种功能,但实际上支持。