簡體   English   中英

LINQ選擇與匿名類型不同

[英]LINQ Select Distinct with Anonymous Types

所以我有一組對象。 確切的類型並不重要。 從中我想提取一對特定屬性的所有唯一對,因此:

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

所以我的問題是:在這種情況下會不會使用默認對象equals(這對我來說沒用,因為每個對象都是新的)或者可以告訴它做一個不同的equals(在這種情況下,Alpha和Bravo的值相等) =>相等的實例)? 有沒有辦法實現這個結果,如果不這樣做的話?

請閱讀K. Scott Allen的精彩帖子:

所有人的平等......匿名類型

簡短的回答(我引用):

事實證明,C#編譯器會覆蓋匿名類型的Equals和GetHashCode。 兩個重寫方法的實現使用類型上的所有公共屬性來計算對象的哈希代碼並測試相等性。 如果同一匿名類型的兩個對象的屬性具有相同的值 - 則對象相等。

因此,對返回匿名類型的查詢使用Distinct()方法是完全安全的。

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();

對不起早先搞砸了格式化

有趣的是它在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()

我運行了一點測試,發現如果屬性是值類型,它似乎工作正常。 如果它們不是值類型,那么類型需要為它提供自己的Equals和GetHashCode實現。 我想,字符串可以工作。

您可以創建自己的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();

為了使它在VB.NET中工作,您需要在匿名類型中的每個屬性之前指定Key關鍵字,如下所示:

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

我正在努力解決這個問題,我認為VB.NET不支持這種類型的功能,但實際上它確實如此。

如果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];
    }
}

嘿,我遇到了同樣的問題,我找到了解決方案。 您必須實現IEquatable接口或簡單地覆蓋(Equals和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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM