2009-07-26 16 views
28

Dato un semplice gerarchia di ereditarietà: persona -> studente, insegnante, PersonaleLINQ: Da un elenco di tipo T, recuperare solo gli oggetti di una certa sottoclasse S

dire che ho un elenco di persone, L. In quella lista ci sono alcuni studenti, insegnanti e personale.

Utilizzando LINQ e C#, c'è un modo per scrivere un metodo che recuperi solo un particolare tipo di persona?

So che posso fare qualcosa di simile:

var peopleIWant = L.OfType<Teacher>(); 

ma voglio essere in grado di fare qualcosa di più dinamico. Mi piacerebbe scrivere un metodo che recuperi i risultati per qualsiasi tipo di persona che potrei pensare, senza dover scrivere un metodo per ogni tipo possibile.

+1

Perché non creare un metodo generico? –

+0

Penso che sia quello che ha fatto Mladen Prajdic. Non ci avevo nemmeno pensato, ma ora che lo vedo, sembra molto ragionevole. – Hythloth

risposta

41

si può fare questo:

IList<Person> persons = new List<Person>(); 

public IList<T> GetPersons<T>() where T : Person 
{ 
    return persons.OfType<T>().ToList(); 
} 

IList<Student> students = GetPersons<Student>(); 
IList<Teacher> teacher = GetPersons<Teacher>(); 

EDIT: aggiunto il vincolo dove.

+0

Ah, sembra meraviglioso. Sono nuovo di LINQ, e non particolarmente esperto in generici, ma questo sembra essere esattamente quello che voglio. Grazie!. – Hythloth

+0

Non è necessario chiamare ToList sul risultato di GetPersons , poiché restituisce già un IList ... –

+1

Si potrebbe voler mettere un vincolo di "dove T: Persona" solo per evitare liste vuote a causa di errori di battitura ecc. –

7

Questo dovrebbe fare il trucco.

var students = persons.Where(p => p.GetType() == typeof(Student)); 
+0

Grazie per l'input. Sapevo che qualcosa del genere era possibile; Volevo estendere l'idea a qualcosa di più generico. Mladen Prajdic mi ha suggerito di usare un metodo generico, che è qualcosa che non mi è passato per la testa. – Hythloth

2

Si potrebbe fare questo:

IEnumerable<Person> GetPeopleOfType<T>(IEnumerable<Person> list) 
    where T : Person 
{ 
    return list.Where(p => p.GetType() == typeof(T)); 
} 

Ma tutto quello che hai fatto è davvero riscrivere il metodo() con una versione più sicura che utilizza tipo statico controllando si passa in una persona di LINQ OfType. Non è ancora possibile utilizzare questo metodo con un tipo determinato in fase di esecuzione (a meno che non si utilizzi il reflection).

Per questo, invece di usare farmaci generici, dovrete fare la variabile di tipo di un parametro:

IEnumerable<Person> GetPeopleOfType(IEnumerable<Person> list, Type type) 
{ 
    if (!typeof(Person).IsAssignableFrom(type)) 
     throw new ArgumentException("Parameter 'type' is not a Person"); 

    return list.Where(p => p.GetType() == type); 
} 

Ora è possibile costruire un certo tipo in modo dinamico e utilizzarlo per chiamare questo metodo.

+0

Grazie per avermi introdotto il vincolo "dove" per i generici. Non appena ho specificato questo vincolo, ora posso contare sul supporto Intellisense per i metodi e le proprietà disponibili del mio tipo Person. – Hythloth

0

Per l'elenco generale, utilizzando delegate:

public List<T> FilterByType(List<T> items, Type filterType) 
{ 
    return items.FindAll(delegate(T t) 
    { 
     return t.GetType() == filterType; 
    }); 
} 
Problemi correlati