LINQ İsimsiz Türlerle Farklı Seçin


150

Bir nesne koleksiyonum var. Kesin türü önemli değil. Ondan bir çift belirli özelliklerin tüm benzersiz çiftlerini çıkarmak istiyorum, böylece:

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

Yani sorum şu: Farklı bu durumda varsayılan nesne eşitlerini kullanacak mı (her nesne yeni olduğu için benim için işe yaramaz) ya da farklı bir eşitlik (bu durumda Alpha ve Bravo'nun eşit değerleri) söylenebilir mi? => eşit örnekler)? Bu sonuca ulaşmanın bir yolu var mı, eğer bunu yapmazsa?


Bu LINQ-to-Objects veya LINQ-to-SQL mi? Sadece itiraz ederseniz, muhtemelen şansınız kalmaz. Ancak, L2S, DISTINCT SQL deyimi geçirilecek gibi, o zaman işe yarayabilir.
James Curran

Yanıtlar:


188

K. Scott Allen'ın mükemmel gönderisini buradan okuyabilirsiniz:

Herkes İçin Eşitlik ... Anonim Türler

Kısa cevap (ve ben alıntı):

Anonim türler için C # derleyicisi Eşittir ve GetHashCode'u geçersiz kılar. Geçersiz kılınan iki yöntemin uygulanması, bir nesnenin karma kodunu hesaplamak ve eşitliği test etmek için türdeki tüm genel özellikleri kullanır. Aynı anonim türdeki iki nesne, özellikleri için aynı değerlere sahipse - nesneler eşittir.

Bu nedenle, anonim türleri döndüren bir sorguda Distinct () yöntemini kullanmak tamamen güvenlidir.


2
Bu sadece bence, eğer özelliklerin kendileri değer türleri ise ya da değer eşitliğini uygularlarsa - cevabımı görün.
tvanfosson

Evet, her mülkte GetHashCode kullandığından, yalnızca her mülkün kendine özgü bir uygulaması varsa işe yarayacaktır. Çoğu kullanım senaryosunun özellik olarak sadece basit türleri içerdiğini düşünüyorum, bu yüzden genellikle güvenlidir.
Matt Hamilton

4
Anonim türlerden ikisinin eşitliğinin üyelerin eşitliğine bağlı olduğu anlamına gelir, çünkü benim için iyi, üyeler alabileceğim bir yer tanımlanır ve gerekirse eşitliği geçersiz kılabilirim. Ben sadece eşitleri geçersiz kılmak için bunun için bir sınıf oluşturmak zorunda değildi.
GWLlosa

3
MS'in VB'nin sahip olduğu "anahtar" sözdizimini tanıtmak için dilekçe vermeye değer olabilir (burada anonim türün belirli özelliklerini 'birincil anahtar' olarak belirtebilirsiniz - bağlı olduğum blog yayınına bakın).
Matt Hamilton

1
Çok ilginç bir yazı. Teşekkürler!
Alexander Prokofyev

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

Daha önce biçimlendirilmiş biçimlendirme için üzgünüm


Bu Uzantılar tür objectve işleyemez object. Her ikisi de objectise string, yinelenen satırları döndürür. FirstNameİs typeof komutunu deneyin ve orada objectaynısını atayın string.
CallMeLaNN

Bu, yazılan nesneler için harika bir yanıttır, ancak anonim türler için gerekli değildir.
crokusek

5

İlginçtir ki C # ile çalışıyor ama VB'de değil

26 harfi döndürür:

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

52 döndürür ...

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

11
KeyAnahtar kelimeyi anonim türe eklerseniz, .Distinct()amaçlandığı gibi çalışır (ör. New With { Key .lower = x.ToString.ToLower(), Key .upper = x.ToString.ToUpper()}).
Cᴏʀʏ

3
Cory haklı. C # kodunun doğru çeviri new {A = b}olduğunu New {Key .A = b}. Anonim VB sınıflarındaki anahtar olmayan özellikler değiştirilebilir, bu yüzden referans ile karşılaştırılırlar. C # 'da, anonim sınıfların tüm özellikleri değiştirilemez.
Heinzi

4

Biraz test yaptım ve özellikleri değer türleri ise, Tamam çalışıyor gibi bulundu. Değer türleri değilse, türün çalışması için kendi Equals ve GetHashCode uygulamaları gerekir. Sanırım dizeler işe yarardı.


2

Lambda ifadesini alan kendi Farklı Uzantı yönteminizi oluşturabilirsiniz. İşte bir örnek

IEqualityComparer arabiriminden türeyen bir sınıf oluşturma

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

Ardından Farklı Uzantı yönteminizi oluşturun

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

ve farklı öğeleri bulmak için bu yöntemi kullanabilirsiniz

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

Bu Uzantılar tür objectve işleyemez object. Her ikisi de objectise string, yinelenen satırları döndürür. FirstNameİs typeof komutunu deneyin ve orada objectaynısını atayın string.
CallMeLaNN

0

Eğer Alphave Bravoortak bir sınıftan hem devralır, sen uygulayarak üst sınıfında eşitliği denetimini dikte mümkün olacak IEquatable<T>.

Örneğin:

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

yani IEquatable <T> uygulayan anonim tip sınıflarınızın özellikleri olarak kullanırsanız, varsayılan davranış yerine Equals çağrılır (yansıma yoluyla tüm genel özelliklerin kontrolü?)
D_Guidi

0

Hey, aynı problemi yaşadım ve bir çözüm buldum. IEquatable arabirimini uygulamanız veya (Equals & GetHashCode) Yöntemlerini geçersiz kılmanız gerekir. Ama bu hile değil, hile GetHashCode yönteminde geliyor. Sınıfınızın nesnesinin karma kodunu döndürmemelisiniz, ancak böyle karşılaştırmak istediğiniz özelliğin karma değerini döndürmelisiniz.

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

Gördüğünüz gibi kişi 3 özellik (Ad, Yaş, IsEgyptian "Çünkü ben") var denilen bir sınıf var GetHashCode içinde ben Kişi nesnesi değil Name özelliği hash döndü.

Deneyin ve ISA çalışacaktır. Teşekkürler Modather Sadik


1
GetHashCode eşitlik karşılaştırmasında kullanılan alanların ve özelliklerin tümünü, yalnızca bir tanesini değil, aynı şekilde kullanmalıdır. iepublic override int GetHashCode() { return this.Name.GetHashCode() ^ this.Age.GetHashCode() ^ this.IsEgyptian.GetHashCode(); }
SD'deki JG

İyi bir karma algoritma oluşturma hakkında bilgi için: stackoverflow.com/questions/263400/…
JG

0

VB.NET'te çalışabilmesi için Key, anonim türdeki her özellikten önce anahtar kelimeyi aşağıdaki gibi belirtmeniz gerekir :

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

Bununla mücadele ediyordum, VB.NET'in bu tür özellikleri desteklemediğini düşündüm, ama aslında öyle.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.