2013-03-01 17 views
9

W moim projekcie mam MyClass, który implementuje IMyClass. Muszę zwrócić listę IMyClass, przekształcając listę innych elementów. Dla uproszczenia załóżmy, że mogę utworzyć MyClass po prostu przekazując inny element do jego konstruktora, tj. new MyClass(item).Czy powinienem rzucić w moje lambda lub rzucić IEnumerable?

Rozważmy następujące dwa wiersze, które (o ile wiem) produkują ten sam wynik:

var option1 = items.Select(item => new MyClass(item)).Cast<IMyClass>().ToList() 
var option2 = items.Select(item => new MyClass(item) as IMyClass).ToList() 

Wydaje mi się, że opcja nr 1 wymagałoby podwójne wyliczenie, raz do oddania wszystkie pozycje do mojego interfejsu i raz, aby wygenerować listę. Jeśli mam rację, opcja nr 2 byłaby mądrzejsza. Jednakże, mam nigdy widziałem jakikolwiek kod używając czegoś jak opcja # 2, i staram się zakładać, że nie jestem wystarczająco inteligentny, by wymyślić coś sprytnego, że reszta społeczności C# nie.

Na marginesie myślę, że opcja nr 2 jest bardziej estetyczna, ale to tylko ja.

Moje pytanie brzmi: jest moją opcją nr 2 lepszym pomysłem, jak myślę, że jest? Czy są jakieś zaginięcia, których mi brakuje, lub inne powody, dla których chciałbym pozostać przy opcji nr 1? A może porównuję dwa głupie pomysły, gdy brakuje mądrzejszego trzeciego, którego kompletnie brakuje?

+3

Nie zapominaj, że przeliczniki są leniwe i kompozycyjne! Opcja 1 nie spowoduje dwukrotnego wyliczenia jej ... –

+0

Dlaczego musisz rzutować, jeśli 'MyClass' implementuje' IMyClass'? –

+1

Wiesz, że zawsze możesz przeliterować typy bez wnioskowania? To znaczy, 'var result = items.Wybierz (x => new MyClass (x)). ToList();' To zwróci 'IEnumerable ' :) –

Odpowiedz

17

pójdę do Wariant 3:

var option3 = items.Select<Foo, IMyClass>(item => new MyClass(item)) 
        .ToList() 

Alternatywnie, nie używaj as ale po prostu rzucić normalnie:

var option4 = items.Select(item => (IMyClass) new MyClass(item)) 
        .ToList() 

Obie te wydają się czystsze niż przy użyciu Cast.

Aha, i jak z C# 4 z .NET 4 (z powodu kowariancji), można umieścić argument typu na wezwanie ToList Zamiast:

var option5 = items.Select(item => new MyClass(item)) 
        .ToList<IMyClass>() 
+0

Opcje 3 i 4 nie zrobiły tego dla mnie estetycznie, ale opcja 5 zdecydowanie to robi. Brawo do wyboru! – ean5533

+0

Nie należy opcji 4 być 'var option4 = items.Select (item => (** IMyClass **) new MyClass (element))'? – pescolino

+0

@pescolino: Tak, rzeczywiście - naprawiony, dzięki. –

3

Wydaje mi się, że opcja nr 1 wymagałoby podwójne wyliczenie

nie jest to prawdą. W obu przypadkach kolekcja items jest wyliczana tylko po uzyskaniu ToList().

Linia

var option1 = items.Select(item => new MyClass(item)).Cast<IMyClass>().ToList() 

jest równoważna

var option1 = items.Select(item => new MyClass(item)).Select(x => (IMyClass)x).ToList() 

Jedyną różnicą między nimi jest, że pierwszy z nich wymaga dwóch wywołań funkcji za sztukę (chyba, że ​​C# inlines z lambdy jakoś, co ja nie wierz w to), podczas gdy druga opcja wymaga tylko jednego.

Osobiście, chciałbym pójść z drugim jako kwestią stylu.

+0

+1. Mój osobisty powód, by wybrać 2 - nie lubię "Cast" w takim kontekście. Pokazuje to, że nie można było zdecydować, jakie przedmioty umieścić w kolekcji. Zauważ, że w wielu przypadkach, jeśli nie potrzebujesz szczególnego typu 'List ' możesz użyć 'IEnumerable ' w miejscach, gdzie wymagane jest 'IEnumerable ' - więc możesz rzeczywiście potrzebować rzutowania w niektórych przypadkach, aby kod był łatwiejszy do odczytania . –

1

Który z nich używasz to kwestia preferencji, coś, czego naprawdę nie możemy ci odpowiedzieć.

Ale twoja intuicja, jeśli jest w pewnym sensie poprawna, że ​​Cast dodaje drugą warstwę iteracji do twojej pętli. Jest to bardzo niewielkie, i wątpię, będzie produkować dowolną mierzalną różnicę w wydajności, ale metoda Cast zwraca nowy obiekt, który w zasadzie IEnumerable wykonuje to:

foreach (object obj in source) yield return (TResult)obj; 

Efekt jest przeważnie inny poziom na stosie wywołań; ponieważ używa on yield, będzie on tylko iterować na żądanie, podobnie jak większość innych metod IEnumerable. Ale będzie musiał powrócić przez dwa poziomy stanu iteratora zamiast jednego. Czy to ma znaczenie dla Ciebie, to coś, co musisz zmierzyć dla własnych aplikacji.

(również pamiętać, że, przynajmniej w odniesieniu do źródła referencyjnego, to jednak niebezpieczna cast, który może wyjątek jeśli obsada jest nieprawidłowy. To kolejny powód, aby wolą opcję 2 #).

1

Zawsze można dostarczyć jednoznacznych argumentów typu, aby Twój Wybór

var option2 = items.Select<IItem,IMyClass>(item => new MyClass(item)).ToList(); 

gdzie IItem to rodzaj lub interfejsu, które mogą być rzucane przedmioty.

Powiązane problemy