views:

124

answers:

3

In my program, I have a class A which is extended by B, C and many more classes. I have a method GetInstance() which returns a instance of B or C (or of one of the other child), but I don't know which one, so the return type of the method is A.

In the method CreateGenericList(), I have a variable v of type A, which is in fact either a B, a C or another child type and I want to create a generic list of the proper type, i.e. List<B> if v is a B or List<C> if v is a C, ...

Currently I do it by using reflection, which works, but this is extremely slow. I wanted to know if there is another way to to it, which doesn't use reflection.

Here is an example of the code of my problem:

class A { }  
class B : A { }  
class C : A { }
// More childs of A.


class Program
{
    static A GetInstance()
    {
        // returns an instance of B or C
    }

    static void CreateGenericList()
    {
        A v = Program.GetInstance();
        IList genericList = // Here I want an instance of List<B> or List<C> or ... depending of the real type of v, not a List<A>.
    }
}

I tried the following hack. I call the following method, hoping the type inferencer will guess the type of model, but it doesn't work and return a List<A>. I believe that because c# is statically typed, T is resolved as A and not as the real type of model at runtime.

static List<T> CreateGenericListFromModel<T>(T model) where T : A
{
    return new List<T> ();
}

Does anybody have a solution to that problem that doesn't use reflection or that it is impossible to solve that problem without reflection?

Thank you very much.

+5  A: 

You can't solve this Problem without reflection, as generic type parameters have to be known at compile time. If you know what type you have (eg. by if(v[0] is B)) you can use v.Cast<B>() to convert it from a List<A> to IEnumerable<B>, which you can turn into a list again by calling ToList().

Femaref
+1, Correct, also a good advise with Cast and ToList.
Stefan Steinegger
Thanks a lot for the answer. Unfortunately your advice won't work in my case, but at least now I'm sure it is not possible to do what I wanted without reflection.
Marc Bettex
+2  A: 

This seems like a bit of a code smell to me. I would suggest that you need to go back to your design to think about why you would really need to do this. The reason this isn't simple to do is because what you are trying to do does violate object oriented principles.

If you are following the SOLID principles, your code should never need to know if what it thought was class A was actually class B or class C.

How will it matter further down the line if your list is B or C? Do you have code like :

if (x is B)
{
  // do stuff to B
}
else
{
  // do stuff to C
}

If you do, then watch out. Avoid doing this! What should happen when you get class D that you have to handle as well?

I would suggest just using a List of A should be all you need to do. Any differences in functionality between B or C should be handled by overriding methods defined within A (Polymorphism).

Mongus Pong
Actually, I have a good reason to use this design in this case, but it is not apparent in my simplified example. But you're right, in other cases I would avoid to do this kind of things.
Marc Bettex
A: 

to expand on Femaref's anwerer what you are trying to do is make your List<T> contravariant over assignment C# 4 does have support for covariant and contravariant generic types. however...

in C# 4 List is covariant (you can assign a List<B> to a List<A> but not the other way around), before C# 4 List<A> and List<B> are seperate types and can't be use together at all (they are invariant over all operations)

however c# 4 does contain something which may help you. dynamic! you can (AFAIK) build a List<dynamic> instead of your List<A> which would then let you access each list item according to its runtime type rather than compiletime. this is essentially similar to the reflection way of doing things but the code would probably be a bit neater.

jk
I cannot solve my problem with dynamic, but I can use the covariant property somewhere else in my code which removes that problem. So thanks a lot for your answer.
Marc Bettex
Classes do not support variance annotations, and even if they did,`List<T>` could not be covariant or contravariant since `T` is used as a parameter type of some methods and a return type of others. `IEnumerable<T>` is covariant in .NET 4.0, however.
kvb
quite right youd be assigning via an IEnumerable.
jk