2010-01-15 26 views
5

Każdy element ma interfejs, IItem. Oprócz tego istnieje interfejs znany jako IDrawableItem, który dziedziczy po Item.Dziedziczenie i dziedziczenie interfejsu

Poniższy kod próbuje narysować element do pobrania, ale nie może tak, jak w kolekcji tej, że sklepy tej klasy akceptują tylko IItem. Do tej klasy można dodać wszystko, co dziedziczy od IItem, ale inne metody można uzyskać tylko za pomocą rzutowania.

foreach (var item in Items) { 
    item.Draw();    // The casting would go here. 
} 

Wiem, jak obsady, as itp ... ale czy jest to do przyjęcia? Czy to najlepsza praktyka?

Zastanawiasz się, czy istnieją inne sposoby radzenia sobie z takimi scenariuszami.

+0

Poniższy kod próbuje narysować element, który może być losowany, ale nie może być zbiorem, który zapisuje w tej klasie tylko IItem. - CZEMU? Opowiedz nam trochę o projekcie. Czy możesz to zmienić? –

+0

Kod Jason pozwoli ci wyjść z dylematu. Chodzi o to, czy jest to odpowiedni projekt dla twoich obiektów, i to jest naprawdę trudne z 2 linii opisu. – hackerhasid

+0

Być może użyj kolekcji, która przechowuje IDrawbleItem s zamiast? –

Odpowiedz

11

Zastosowanie Enumerable.OfType wyodrębnić tylko te elementy Items implementujące IDrawableItem:

foreach(var item in Items.OfType<IDrawableItem>()) { 
    item.Draw(); 
} 

Aby odpowiedzieć na pytanie, które Nanook hasła w komentarzach powyższy kod będzie prawdopodobnie być tłumaczone na kod równoważna następującej:

foreach(var item in Items) { 
    if(item is IDrawableItem) { 
     ((IDrawable)item).Draw(); 
    } 
} 

oczywiście, naprawdę istnieje iterator za kulisami, który wygląda mniej więcej tak:

public static IEnumerable<T> OfType<T>(this IEnumerable<TSource> source) { 
    if(source == null) { 
     throw new ArgumentNullException("source"); 
    } 
    foreach(TSource item in source) { 
     if(item is T) { 
      yield return (T)item; 
     } 
    } 
} 

To, co to pokazuje, to, że prawdopodobnie wykonujemy tylko iterację przez Items jeden raz. Oczywiście nie ma wymogu, aby implementacja była przeprowadzana jak wyżej, ale jest to rozsądne.

+0

Czy ten przykład faktycznie wykonuje 2 pętle? 1, aby uzyskać IDrawableItems, a drugi przepuścić przez nie i zadzwonić Draw. Jeśli tak, to nie jest to bardzo wydajne. – Nanook

+2

Nie, przesyła wyniki. –

+0

Nop, nie ma. Wszystkie metody Linq.Enumerable są implementowane za pomocą niestandardowego modułu wyliczającego. Powyższy kod przełożyłby się na coś takiego jak foreach (element var w elementach) {if item is IDrawableItem yield return item jako IDrawableItem; } – herzmeister

1

dwa alternatywne rozwiązania:

  • Przechowywać z kanału alfa w osobnej kolekcji.
  • Dodaj metodę DrawIfPossible() do IItem. IDrawableItem powinien go zastąpić, aby zadzwonić pod numer Draw(), inny implementator powinien mieć pustą implementację.

Jawne wyrażenie typu jest uważane za znak, że może być coś nie tak z projektem.

+0

Jak już powiedziałem, jest to szybki prototyp, który prawdopodobnie ulegnie zmianie. Chociaż zgadzam się z twoim ostatnim punktem dotyczącym projektu. – Finglas

+0

Nie była przeznaczona jako krytyka. Są przypadki, gdy konieczne jest wpisywanie zapytań typu. Szybkie hacki, frameworki przekazujące obiekty jako 'ICastItDownAsNeeded', itp. –