Bir Koleksiyona Aralık Ekle


109

Bir iş arkadaşım bugün bana bir koleksiyona nasıl aralık ekleyebileceğimi sordu. Miras alan bir sınıfı var Collection<T>. Zaten bazı öğeleri içeren bu türden bir get-only özelliği vardır. Mülk koleksiyonuna başka bir koleksiyondaki öğeleri eklemek istiyor. Bunu C # 3 dostu bir şekilde nasıl yapabilir? (Union yapma ve yeniden atama gibi çözümleri engelleyen get-only özelliğiyle ilgili kısıtlamaya dikkat edin.)

Elbette, Mülkiyetli bir foreach. Ekle çalışacaktır. Ama bir- List<T>tarzlı AddRange çok daha zarif olurdu.

Bir uzatma yöntemi yazmak yeterince kolaydır:

public static class CollectionHelpers
{
    public static void AddRange<T>(this ICollection<T> destination,
                                   IEnumerable<T> source)
    {
        foreach (T item in source)
        {
            destination.Add(item);
        }
    }
}

Ama tekerleği yeniden icat ettiğimi hissediyorum. Hiçbir şey benzer in bulamadık System.Linqya morelinq .

Kötü tasarım? Sadece Arayın Ekle? Bariz olanı mı kaçırıyorsunuz?


5
LINQ'dan gelen Q'nun 'sorgu' olduğunu ve gerçekten veri alma, projeksiyon, dönüştürme vb. İle ilgili olduğunu unutmayın. Mevcut koleksiyonları değiştirmek, LINQ'nun amaçlanan amacına dahil değildir, bu nedenle LINQ hiçbir şey sağlamaz. bunun için kutunun dışında. Ancak uzatma yöntemleri (ve özellikle sizin örneğiniz) bunun için ideal olacaktır.
Levi

Bir problem, ICollection<T>bir Addmetodu yok gibi görünüyor . msdn.microsoft.com/en-us/library/… Ancak Collection<T>bir tane var.
Tim Goodman

@TimGoodman - Bu genel olmayan bir arayüz. Bkz msdn.microsoft.com/en-us/library/92t2ye13.aspx
TrueWill

"Mevcut koleksiyonları değiştirmek, LINQ'nun amaçlanan amacına gerçekten girmiyor". @Levi O halde neden Add(T item)ilk etapta var? Tek bir öğe ekleme ve ardından tüm arayanların aynı anda birden fazla öğe eklemek için yineleme yapmasını beklemek için yarı pişmiş bir yaklaşım gibi görünüyor. İfadeniz kesinlikle doğru IEnumerable<T>ama kendimi ICollectionsbirden fazla kez hayal kırıklığına uğratmış buldum . Sana katılmıyorum, sadece havalandırıyorum.
akousmata

Yanıtlar:


62

Hayır, bu tamamen makul görünüyor. Temelde sadece bunu yapan, ancak koleksiyonunuzun somut olmasını gerektiren bir List<T>.AddRange () yöntemi vardır List<T>.


1
Teşekkürler; çok doğrudur, ancak çoğu genel mülk MS yönergelerine uyar ve Listeler değildir.
TrueWill

7
Evet - Bunu yapmakta neden bir sorun olduğunu düşünmediğimin gerekçesini daha çok veriyordum. List <T> sürümünden daha az verimli olacağının farkına varın (çünkü <T> listesi önceden tahsis edebilir)
Reed Copsey

NET Core 2.2'deki AddRange yönteminin, bu sayıda gösterildiği gibi, yanlış kullanıldığında garip bir davranış gösterebileceğine dikkat edin: github.com/dotnet/core/issues/2667
Bruno

36

Döngüyü çalıştırmadan önce uzantı yönteminde List'e yayınlamayı deneyin. Bu şekilde List.AddRange'ın performansından yararlanabilirsiniz.

public static void AddRange<T>(this ICollection<T> destination,
                               IEnumerable<T> source)
{
    List<T> list = destination as List<T>;

    if (list != null)
    {
        list.AddRange(source);
    }
    else
    {
        foreach (T item in source)
        {
            destination.Add(item);
        }
    }
}

2
asOperatör atmak asla. Eğer destinationdökme olamaz, listboş olacak ve elseblok çalıştırır.
rymdsmurf

4
arrgggh! Kutsal olan her şeyin aşkı için koşul dallarını değiştirin!
nicodemus13

13
Aslında ciddiyim. Asıl neden, fazladan bilişsel yük olması, ki bu genellikle gerçekten oldukça zor. Sürekli olarak olumsuz koşulları değerlendirmeye çalışıyorsunuz, ki bu genellikle nispeten zor, zaten her iki dalınız da var, (IMO) 'eğer boşsa' bunu yap, yoksa bunu yap 'demek, tersini yapmaktan daha kolaydır. Aynı zamanda varsayılanlarla da ilgilidir, olabildiğince sık olumlu kavram olmalıdırlar, örneğin "if (! Şey.IsDisabled) {} ​​else {} 'durmanızı ve' ah, değil devre dışı bırakıldı anlamına gelir 'diye düşünmenizi gerektirir, değil mi? anladım, yani diğer dal devre dışı bırakıldığında). Ayrıştırılması zor.
nicodemus13

13
"Bir şeyi! = Boş" yorumlamak, "bir şey == boş" yorumlamaktan daha zor değildir. Olumsuzlama operatörüdür ancak tamamen farklı bir şey ve eğer-else-deyimini yeniden son örnekte elliminate o operatörü. Bu nesnel olarak bir gelişmedir, ancak asıl soruyla ilgili olmayan bir gelişme. Bu özel durumda, iki form kişisel tercihler meselesidir ve yukarıdaki gerekçeye göre "! =" - operatörünü tercih ederim.
rymdsmurf

15
Kalıp eşleştirme herkesi mutlu edecek ... ;-)if (destination is List<T> list)
Jacob Foshee

28

Beri .NET4.5seni tek astar isterseniz kullanabilirsiniz System.Collections.GenericForEach.

source.ForEach(o => destination.Add(o));

hatta daha kısa

source.ForEach(destination.Add);

Performans açısından, her döngü için aynıdır (sözdizimsel şeker).

Ayrıca yok gibi atama deneyin

var x = source.ForEach(destination.Add) 

neden ForEachgeçersizdir.

Düzenleme: Yorumlardan kopyalandı, Lipert'in ForEach hakkındaki görüşü


9
Şahsen bu konuda Lippert ile birlikteyim: blogs.msdn.com/b/ericlippert/archive/2009/05/18/…
TrueWill

1
Source.ForEach (destination.Add) olmalı mı?
Frank

4
ForEachsadece tanımlanmış gibi görünüyor List<T>, değil Collectionmi?
Protector one


Eric Lippert'in blog gönderisine bağlantı güncellendi: Kodlamada Muhteşem Maceralar | "Foreach" - "ForEach"
Alexander

19

Her birinin Addkoleksiyonun kapasitesini kontrol edeceğini ve gerektiğinde (daha yavaş) yeniden boyutlandıracağını unutmayın. İle AddRange, koleksiyon kapasitesi ayarlanacak ve ardından öğeler eklenecektir (daha hızlı). Bu uzatma yöntemi son derece yavaş olacak ama işe yarayacak.


3
Buna ek olarak, AddRange ile toplu bildirimin aksine, her ekleme için bir koleksiyon değişikliği bildirimi de olacaktır.
Nick Udell

3

İşte biraz daha gelişmiş / üretime hazır sürüm:

    public static class CollectionExtensions
    {
        public static TCol AddRange<TCol, TItem>(this TCol destination, IEnumerable<TItem> source)
            where TCol : ICollection<TItem>
        {
            if(destination == null) throw new ArgumentNullException(nameof(destination));
            if(source == null) throw new ArgumentNullException(nameof(source));

            // don't cast to IList to prevent recursion
            if (destination is List<TItem> list)
            {
                list.AddRange(source);
                return destination;
            }

            foreach (var item in source)
            {
                destination.Add(item);
            }

            return destination;
        }
    }

rymdsmurf'un cevabı saf görünebilir, çok basit, ancak heterojen listelerle işe yarıyor. Bu kodun bu kullanım durumunu desteklemesi mümkün müdür?
richardsonwtr

Örneğin: bir soyut sınıf destinationlistesidir Shape. miras alınan bir sınıfın sourcelistesidir Circle.
richardsonwtr

1

C5 Jenerik Koleksiyonları Kütüphane sınıfları tüm desteklemek AddRangeyöntemi. C5, temeldeki uygulamalarının tüm özelliklerini gerçekten ortaya çıkaran ve System.Collections.Generic ICollectionve IListarabirimleriyle arabirim uyumlu olan çok daha sağlam bir arabirime sahiptir; bu , C5koleksiyonların temel uygulama olarak kolayca ikame edilebileceği anlamına gelir .


0

IEnumerable aralığınızı bir listeye ekleyebilir ve ardından ICollection = öğesini listeye ayarlayabilirsiniz.

        IEnumerable<T> source;

        List<item> list = new List<item>();
        list.AddRange(source);

        ICollection<item> destination = list;

3
Bu işlevsel olarak çalışsa da, koleksiyon özelliklerini salt okunur hale getirmek için Microsoft yönergelerini ihlal ediyor ( msdn.microsoft.com/en-us/library/ms182327.aspx )
Nick Udell

0

Veya bunun gibi bir ICollection uzantısı oluşturabilirsiniz:

 public static ICollection<T> AddRange<T>(this ICollection<T> @this, IEnumerable<T> items)
    {
        foreach(var item in items)
        {
            @this.Add(item);
        }

        return @this;
    }

Bunu kullanmak, onu bir listede kullanmak gibi olur:

collectionA.AddRange(IEnumerable<object> items);
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.