Как использовать Activator для создания экземпляра универсального типа и его приведения к этому типу? - PullRequest
35 голосов
/ 04 февраля 2012

У меня есть универсальный тип Store<T>, и я использую Activator, чтобы создать экземпляр этого типа. Как теперь, после использования Активатора, я могу привести полученный объект типа object обратно к созданному типу? Я знаю тип, который я использовал для создания экземпляра универсального. Пожалуйста, смотрите следующий код:

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

Я хотел бы сделать что-то вроде этого:

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

но это не работает по понятным причинам. В качестве альтернативы я попробовал:

var store = (Store<IStorable>)x;

, который мог бы, по моему мнению, работать, но дает InvalidCastException.

Как снова получить доступ к известным мне Store<T> методам в объекте x?

Ответы [ 5 ]

30 голосов
/ 04 февраля 2012

Поскольку фактический тип T доступен только через отражение, вам также потребуется доступ к методам Store<T> через отражение:

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

РЕДАКТИРОВАТЬ Вы также можете определить дополнительный интерфейс IStore, который не использует обобщенные значения и использует вместо него IStorable:

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

Ваш Store<T> будет оставаться общим, но вы получите доступ к его CountItems, переведя на IStore:

var x = (IStore)Activator.CreateInstance(constructedType, new object[] { someParameter });
var count = x.CountItems((IStorable)someObjectThatImplementsStorable);
4 голосов
/ 04 февраля 2012

Не можете ли вы просто обернуть это?

что-то вроде

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

или мне не хватает того, что вы пытаетесь сделать?

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
{ }

печать

1 
ConsoleApp1.Store'1[ConsoleApp1.Beer]
3 голосов
/ 04 февраля 2012

Самый подходящий ответ, на мой взгляд, был бы: «Вы не можете сделать это таким образом».

Вы можете попробовать ввести интерфейс IStorage и попытаться сделать его ковариантным или контравариантным (вы видели этот вариант?).Если это не вариант, например, если у вас есть и универсальные типы ввода и вывода, используемые в Storage, то нет способа реализовать то, что вы хотите.Причина в том, что Storage<Beer> нельзя безопасно использовать как Storage<IStorable> из-за этого случая:

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

Единственный возможный обходной путь для вас, как я вижу, состоит в том, чтобы переместить приведение из этого метода и приведение объектав месте, где вы знаете все точные типы:

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));
1 голос
/ 06 декабря 2013

T должен быть типом Store, избегающим использования typeof (Store

0 голосов
/ 04 февраля 2012

Допустим, что someObjectThatImplementsIStorable имеет тип MyStorable.

например. MyStorable someObjectThatImplementsIStorable = new MyStorable (); ... // остальная часть вашего кода здесь.

Тогда x не может быть брошен в Store, но он может быть брошен в Store. Будет работать следующее: (Магазин) x

Обратите внимание, что хотя MyStorable реализует IStorable, между Store и Store нет никакой связи. Это два разных класса, которые не являются производными друг от друга.

и.

...