50

I have a generic type Store<T> and use Activator to make an instance of this type. Now how, after using the Activator, can I cast the resulted object of type object back to the instantiated type? I know the type that I used to instantiate the generic. Please see the following code:

class Store<T> where T : IStorable 
{}

class Beer : IStorable 
{}

class BeerStore : Store<Beer>
{}

Type storeType = someObjectThatImplementsIStorable.GetType();
Type classType = typeof(Store<>);
Type[] typeParams = new Type[] { storeType };   
Type constructedType = classType.MakeGenericType(typeParams);

object x = Activator.CreateInstance(constructedType, new object[] { someParameter });

What I would like to do is something like this:

var store = (Store<typeof(objectThatImplementsIStorable)>)x;

but that doesn't work for obvious reasons. As an alternative I tried:

var store = (Store<IStorable>)x;

which could possibly work in my opinion, but gives an InvalidCastException.

How do I get access again to the Store<T> methods that I know are in the object x?

Bazzz
  • 26,427
  • 12
  • 52
  • 69
  • Can you make `obvious` reason more obvious? Or just tell us what is the reason. Or do you really mean `typeof...`, not substituting it with your type? – Snowbear Feb 04 '12 at 12:35
  • 1
    @Snowbear that piece of code doesn't compile because Generics don't allow instances of Types to be used as T. I don't know that type, it could be any object that implements the IStorable interface. – Bazzz Feb 04 '12 at 12:37
  • What type does the debugger think x is? Is it the right type? – Phil C Feb 04 '12 at 12:41
  • 1
    @Carnotaurus, yes the debugger thinks the correct type `Store`. So the Activator instantiates the correct object but boxes it in an `object`, and now I would like to unbox it again. – Bazzz Feb 04 '12 at 12:49
  • @Bazzz box is not the correct term, unless the generic Store type is a struct. – phoog Feb 05 '12 at 03:17
  • @phoog, thanks for your correction. It's not a Struct, what would be the correct term to use then? – Bazzz Feb 05 '12 at 10:07
  • 1
    @Bazzz Unfortunately I can't think of a concise term to replace "box". The key concept here is that the conversion for reference types is a *reference conversion*, while a boxing or unboxing conversion is not. I'd change the wording to "the activator instantiates the correct type, but returns a reference of type `object`, and now I would like to downcast it" (or something like that). – phoog Feb 05 '12 at 18:26

5 Answers5

40

Since the actual type T is available to you only through reflection, you would need to access methods of Store<T> through reflection as well:

Type constructedType = classType.MakeGenericType(typeParams);

object x = Activator.CreateInstance(constructedType, new object[] { someParameter });
var method = constructedType.GetMethod("MyMethodTakingT");
var res = method.Invoke(x, new object[] {someObjectThatImplementsStorable});

EDIT You could also define an additional IStore interface that does not use generics, and uses IStorable instead:

interface IStore {
    int CountItems(IStorable item);
}
class Store<T> : IStore where T : IStorable {
    int CountItems(IStorable item) {
        return count;
    }
}

Your Store<T> would remain generic, but you would get access to its CountItems by casting to IStore:

var x = (IStore)Activator.CreateInstance(constructedType, new object[] { someParameter });
var count = x.CountItems((IStorable)someObjectThatImplementsStorable);
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • I feared it would come down to this. Is it a rule of thumb that when I know the actual type T only through reflection that all access to the actual object need to go through reflection as well? – Bazzz Feb 04 '12 at 12:50
  • @Bazzz Yes, if the only thing you know about `someObjectThatImplementsIStorable` is that it is an `object` implementing `IStorable`, there is little you can do to avoid reflection. If you were getting `someObjectThatImplementsIStorable` as a generic type `T`, though, you could cast to `Store` as well. An alternative would be to avoid generics in a subset of methods (I'll update the answer shortly to show you how). – Sergey Kalinichenko Feb 04 '12 at 13:01
  • I'll go this way, I know I can make this work, and other options don't seem to be available. Thank you for the answer. I'd like to see the subset though, if you have the time. Just out of interest and to learn something. – Bazzz Feb 04 '12 at 13:04
  • @Bazzz I just updated the answer. I've been using the trick with a generic class implementing a regular interface a lot, it is very powerful. – Sergey Kalinichenko Feb 04 '12 at 13:10
  • @dasblinkenlight where it falls down is anywhere where you consume or act on T ie interface IStore { void Add(object item){}} != IStore { void Add(T item){}}, personally this is prettymuch the only places i use generics anyway – undefined Feb 04 '12 at 13:14
  • @dasblinkenlight Indeed I have attempted this path but it didnt work out for me because my `Store` has a method `public T GetSingleByID(int id)` that I'd like to access. I can never declare this generic method in a non-generic interface, because the interface wouldn't know about T. – Bazzz Feb 06 '12 at 08:13
  • @Bazzz You can still try it by adding a `IStorable GetSingleStorableById(int it)` method to your interface, and implementing it by calling `GetSingleByID`. – Sergey Kalinichenko Feb 06 '12 at 14:03
7

Cant you just wrap it?

something like

public Store<T> IConstructStore<T>(T item) where T : IStorable 
{
 return Activator.CreateInstance(typeof(Store<T>), new object[] { someParameter }) as Store<T>;
}

or am i missing what you are trying to do?

IE

class Program
{
    static void Main(string[] args)
    {
        Beer b = new Beer();
        var beerStore = IConstructStore(b);
        Console.WriteLine(beerStore.test);
        Console.WriteLine(beerStore.GetType().ToString());
    }

    public static Store<T> IConstructStore<T>(T item) where T : IStorable
    {
        return Activator.CreateInstance(typeof(Store<T>), new object[] { }) as Store<T>;
    }
}

interface IStorable { }

class Store<T> where T : IStorable
{
    public int test = 1;
}

class Beer : IStorable
{ }

prints

1 
ConsoleApp1.Store'1[ConsoleApp1.Beer]
undefined
  • 33,537
  • 22
  • 129
  • 198
  • interesting, but then? How do I call this method? can you add a line of sample code? – Bazzz Feb 04 '12 at 12:45
  • yeah so long as you call it with any object T is infered, ie Beer item = new Beer(); var beerStore= IConstructStore(item); beerStore.whatever() should work fine – undefined Feb 04 '12 at 12:52
  • `typeof(Store)` doesn't compile, neither does `as Store` – Bazzz Feb 04 '12 at 12:55
  • I use Visual Studio 2010 and .Net 4. The compile error is The type 'T' cannot be used as type parameter 'T' in the generic type or method 'Store'. There is no boxing conversion or type parameter conversion from 'T' to 'IStorable'. – Bazzz Feb 04 '12 at 13:06
  • Ah you added the where section now. Maybe that made a difference. Let me try again. – Bazzz Feb 04 '12 at 13:07
  • did you include where T : IStorable on the IConstructStore? – undefined Feb 04 '12 at 13:08
  • Yeah sorry coding from the top of my head – undefined Feb 04 '12 at 13:09
  • Nope no luck, the Activator now makes a `Store` instead of the `Store`. So now it looks like it is working but the Activator makes the wrong object, I'm expecting a `Store` or any other object that implements `IStorable`, but your method returns a `Store` in its stead. – Bazzz Feb 04 '12 at 13:16
  • it doesnt for me, ive added a testcase to my answer – undefined Feb 04 '12 at 13:19
  • I figured out what the difference is, in your case you feed the method with a `Beer`, in my case I feed the method with an `IStorable`, which in fact is a `Beer`, but I don't know that at the time that I am calling the method. So where you have `Beer beer = new Beer();` I have `IStorable objectThatImplementsIStorable;`, we both use this as the `item` parameter. – Bazzz Feb 04 '12 at 14:05
  • I found this answer very helpful for my case. – Charles Owen Apr 06 '21 at 20:58
3

Most appropriate answer in my opinion would be 'you can't do it in this way'.

You might try introducing an interface IStorage and try making it covariant or contravariant (have you seen that option?). If it is not an option, for example if you have both input and output generic types used in Storage, then there is no way to implement what you want. The reason is that Storage<Beer> cannot be safely used as Storage<IStorable> due to this case:

Storage<IStorable> store = new Storage<Beer>(); // let's pretend we can do it 
store.Save(new StorableButNotBeer()); // what will happen here?

The only possible workaround for you as I see is to move casting out from this method and cast the object in the place where you know all the exact types:

public void object CreateStore(Type istorableType)
{
    // here is your activator code, but you will have to return an object
}

var beerStore = (Store<Beer>)CreateStore(typeof(Beer));
Snowbear
  • 16,924
  • 3
  • 43
  • 67
  • Thank you for your suggestion, I do in fact have input and output generic types in Store so that path doesn't work out. As to the other suggestion, besides an instance of the objectThatImplementsIStorable I do not know the actual Type anywhere. I think I'll end up using Reflection on the object that the Activator made. +1 for good explanation of why the issue exists. – Bazzz Feb 04 '12 at 13:03
1

T must be the type Store<X> avoiding the use of typeof(Store<T>

Pang
  • 9,564
  • 146
  • 81
  • 122
0

Let's say that someObjectThatImplementsIStorable is of type MyStorable.

e.g. MyStorable someObjectThatImplementsIStorable = new MyStorable( ); ... // rest of your code here.

Then x cannot be cast to Store, but it can be cast to Store. The following will work: (Store)x

Note that although MyStorable implements IStorable, there is no relationship between Store and Store. These are two distinct classes that do not derive from each other.

u.

Uri London
  • 10,631
  • 5
  • 51
  • 81