views:

885

answers:

3

Given a simple inheritance hierarchy: Person -> Student, Teacher, Staff

Say I have a list of Persons, L. In that list are some Students, Teachers, and Staff.

Using LINQ and C#, is there a way I could write a method that could retrieve only a particular type of person?

I know I can do something like:

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

But I want to be able to do something more dynamic. I would like to write a method that will retrieve results for any type of Person I could think of, without having to write a method for every possible type.

A: 

This should do the trick.

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

kareem
Thank you for your input. I knew that something like this was possible; I wanted to extend the idea to something more generic. Mladen Prajdic suggested I use a Generic Method, which is something that hadn't crossed my mind.
Hythloth
+3  A: 

you can do this:

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: added the where constraint.

Mladen Prajdic
Ah, this looks wonderful. I am new to LINQ, and not particularly well-versed in generics, but this seems to be precisely what I want. Thank you!.
Hythloth
No need to call ToList on the result of GetPersons<T>, since it already returns an IList<T>...
Thomas Levesque
You might want to put a constraint of "where T : Person" just to avoid empty lists due to typos etc.
Jon Skeet
@Thomas Levesque: yeah i saw that and removed it. thanx.
Mladen Prajdic
@Jon Skeet, could you clarify how I would add such a constraint? Do you mean to do some kind of check that "T" is really of type Person, as to avoid a possible logical error? Perhaps a "if(T is Person)" check at the start of this method?
Hythloth
No, Jon is talking about a generic type constraint, which is checked at compile time, not runtime. See this link : http://msdn.microsoft.com/en-us/library/d5x73970.aspx
Thomas Levesque
Am I missing something here? It seems like all we did here was make the call to OfType into a redundant method.
Joe Chung
A: 

You could do this:

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

But all you've really done is rewrite LINQ's OfType() method with a safer version that uses static type checking to ensure you pass in a Person. You still can't use this method with a type that's determined at runtime (unless you use reflection).

For that, rather than using generics, you'll have to make the type variable a parameter:

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

Now you can construct some type dynamically and use it to call this method.

Richard Berg
Hythloth