2012-02-01 12 views
8

Jak mogę odfiltrować obiekty na podstawie ich typu pochodnego za pomocą linq-to-objects?Wyklucz typy z IEnumerable z linq

Szukam rozwiązania o najlepszej wydajności.

Klasy używane:

abstract class Animal { } 
class Dog : Animal { } 
class Cat : Animal { } 
class Duck : Animal { } 
class MadDuck : Duck { } 

wiem z trzech metod: Użyj słowa kluczowego is, użyj metody Except i użyć metody OfType.

List<Animal> animals = new List<Animal> 
{ 
    new Cat(), 
    new Dog(), 
    new Duck(), 
    new MadDuck(), 
}; 

// Get all animals except ducks (and or their derived types) 
var a = animals.Where(animal => (animal is Duck == false)); 
var b = animals.Except((IEnumerable<Animal>)animals.OfType<Duck>()); 

// Other suggestions 
var c = animals.Where(animal => animal.GetType() != typeof(Duck)) 

// Accepted solution 
var d = animals.Where(animal => !(animal is Duck)); 
+2

Co, jeśli masz 'MadDuck', który dziedziczy po' Kaczce'? Czy powinien on zostać zwrócony, czy nie? A co, btw, co próbujesz osiągnąć? Użycie sprawdzania typu "is" i "runtime" może wskazywać problemy projektowe. – Groo

+0

Dobre pytanie. W tym konkretnym przypadku chciałbym, aby wyłączono także podklasy Ducka. To spowodowałoby, że moja opcja "b" jest niepoprawna, ponieważ porównuje typ, ale nie dziedziczenie. – Aphelion

+0

Odnośnie mojego drugiego zadania: z punktu widzenia OOP, inne części twojego kodu nie powinny zwykle zajmować się rzeczywistymi typami twoich obiektów. Powinien wiedzieć "tak mało jak to możliwe". Dlatego zastanawiam się, dlaczego to robisz. – Groo

Odpowiedz

9

Jeśli chcesz również wykluczyć podklasy Duck, a następnie is jest najlepszy. Można skrócić kod tylko .Where(animal => !(animal is Duck));

przeciwnym wypadku zalecenie SLL z dnia gettype najlepiej

+0

Ponieważ chcę wykluczyć podklasy i jest to krótszy i bardziej czytelny zapis, zaakceptowałem twoją odpowiedź. – Aphelion

+0

Nie wierzę, że to bije OfType (). – Will

4
  • rozwiązanie wykorzystujące Except() jest dość ciężki.
  • Należy pamiętać, że rozwiązanie is - wróci tak nawet niektórzy SomeDuck klasy odziedziczone Duck

    class SomeDuck : Duck 
    ... 
    // duck is Duck == true 
    var duck = new SomeDuck(); 
    

Innym rozwiązaniem mogłoby być:

animals.Where(animal => animal.GetType() != typeof(Duck)) 
+1

Dziękuję. Włączyłem twoją sugestię do pytania. – Aphelion

1

Jeśli nie chcesz Duck ani żadnego podklasę Duck zostać zwrócone, należy użyć metody IsAssignableFrom:

animals.Where(animal => !animal.GetType().IsAssignableFrom(typeof(Duck))); 
+0

Nice. Nigdy wcześniej nie używałem 'IsAssignableFrom'. Zastanawiam się, że wydajność jest dla tej metody. Brzmi jak dobry przypadek dla niektórych profili. – Aphelion

+0

Spodziewałam się, że będzie tak szybki, jak "od", a następnie zerowy. –