2009-09-25 14 views
65

Współpracownik poprosił mnie dzisiaj o dodanie zakresu do kolekcji. Ma klasę, która dziedziczy po Collection<T>. Istnieje właściwość typu "tylko pobierz", która zawiera już pewne elementy. Chce dodać przedmioty z innej kolekcji do kolekcji nieruchomości. Jak może to zrobić w przyjazny dla C# 3 sposób? (Należy zwrócić uwagę na ograniczenie dotyczące właściwości "tylko przyzwolenie", która uniemożliwia takie rozwiązania, jak przydział i ponowne przypisanie.)AddRange do kolekcji

Oczywiście, foreach z własnością. Dodaj będzie działać. Ale styl AddRange w stylu List<T> byłby znacznie bardziej elegancki.

To proste wystarczy napisać metodę rozszerzenia:

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

Ale mam wrażenie, że jestem wyważania otwartych drzwi. Nie znalazłem nic podobnego w System.Linq lub morelinq.

Zły projekt? Po prostu zadzwoń Dodaj? Brakujące oczywiste?

+3

Pamiętaj, że Q z LINQ to "zapytanie" i tak naprawdę chodzi o odzyskiwanie danych, rzutowanie, przekształcanie itp.Modyfikowanie istniejących kolekcji naprawdę nie wchodzi w zakres zamierzonego celu LINQ, dlatego też LINQ nie dostarcza niczego poza tym zestawem. Ale metody rozszerzenia (w szczególności Twoja próbka) byłyby idealne do tego. – Levi

+0

Jeden problem, "ICollection " wydaje się nie mieć metody "Dodaj". http://msdn.microsoft.com/en-us/library/system.collections.icollection_methods(v=vs.100).aspx Jednak 'Collection ' ma jeden. –

+0

@TimGoodman - To nietypowy interfejs. Zobacz http://msdn.microsoft.com/en-us/library/92t2ye13.aspx – TrueWill

Odpowiedz

38

Nie, wydaje się to całkowicie uzasadnione. Istnieje metoda List<T>.AddRange(), która zasadniczo to robi, ale wymaga, aby kolekcja była konkretna: List<T>.

+0

Dzięki; bardzo prawdziwe, ale większość właściwości publicznych jest zgodna z wytycznymi MS i nie jest listami. – TrueWill

+4

Tak, podawałem to bardziej jako uzasadnienie, dlaczego nie sądzę, że jest jakiś problem z tym. Po prostu zdaj sobie sprawę, że będzie on mniej wydajny niż wersja List (ponieważ lista może wstępnie przyporządkować). –

1

Klasy C5 Generic Collections Library wszystkie obsługują metodę AddRange. C5 ma znacznie bardziej odporny interfejs, który faktycznie eksponuje wszystkie funkcje podstawowych implementacji i jest kompatybilny z interfejsami System.Collections.GenericICollection i IList, co oznacza, że ​​zbiory C5 mogą być łatwo zastąpione przez podstawowe implementacje.

16

Pamiętaj, że każdy Add sprawdza pojemność kolekcji i zmienia jej rozmiar, gdy jest to konieczne (wolniej). Przy AddRange kolekcja zostanie ustawiona na pojemność, a następnie dodane elementy (szybciej). Ta metoda rozszerzenia będzie bardzo powolna, ale zadziała.

+3

Aby dodać do tego, pojawi się również powiadomienie o zmianie kolekcji dla każdego dodania, w przeciwieństwie do jednego powiadomienia zbiorczego z AddRange. –

0

Możesz dodać swój zakres IEnumerable do listy, a następnie ustawić ICollection = na liście.

 IEnumerable<T> source; 

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

     ICollection<item> destination = list; 
+3

Chociaż funkcjonuje to poprawnie, łamie wytyczne firmy Microsoft, tak aby właściwości kolekcji były tylko do odczytu (http://msdn.microsoft.com/en-us/library/ms182327.aspx). –

24

Spróbuj rzucić na listę w metodzie rozszerzenia przed uruchomieniem pętli. W ten sposób możesz skorzystać z wydajności List.AddRange.

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

To może być trochę pytanie, ale co się stanie, gdy kolekcja '' 'destination''' nie może zostać przeniesiona do' '' List '' '? Czy '' list''' automatycznie staje się '' 'null''' lub jest generowanym wyjątkiem? –

+1

Operator 'as' nigdy nie rzuci. Jeśli 'destination' nie może być rzutowany,' list' będzie null i zostanie wykonany blok 'else'. – rymdsmurf

+3

arrgggh! Zamieńcie gałęzie kondycji, dla miłości wszystkiego, co święte! – nicodemus13

15

Od .NET4.5 jeśli chcesz jedną wkładkę ty can use System.Collections.Generic ForEach.

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

lub nawet krótsze

source.ForEach(destination.Add); 

Performance-mądry to sam, jak dla każdej pętli (cukier syntaktyczny).

także nie próby przypisywania go jak

var x = source.ForEach(destination.Add) 

przyczyny ForEach jest nieaktualna.

+5

Osobiście jestem z Lippertem na ten temat: http://blogs.msdn.com/b/ericlippert/archive /2009/05/18/foreach-vs-foreach.aspx – TrueWill

+1

Powinien to być source.ForEach (destination.Add)? – Frank

+0

@Frank Witaj Frank, dzięki, że zauważyłeś :) –

Powiązane problemy