Как использовать Activator для создания экземпляра родового типа и возврата его к этому типу?
У меня есть общий тип 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
?
Ответы
Ответ 1
Поскольку фактический тип 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});
EDIT. Вы также можете определить дополнительный интерфейс IStore
, который не использует generics, и вместо этого использует 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);
Ответ 2
Не можете ли вы его обернуть?
что-то вроде
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
Наиболее подходящий ответ, на мой взгляд, был бы "вы не можете сделать это таким образом".
Вы можете попробовать ввести интерфейс IStorage
и попытаться сделать его ковариантным или контравариантным (вы видели эту опцию?). Если это не вариант, например, если у вас есть как входные, так и выходные общие типы, используемые в Storage
, тогда нет способа реализовать то, что вы хотите. Причина в том, что Storage<Beer>
нельзя использовать в качестве Storage<IStorable>
из-за этого случая:
Storage<IStorable> store = new Storage<Beer>(); // let 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));
Ответ 4
Скажем, что someObjectThatImplementsIStorable имеет тип MyStorable.
например.
MyStorable someObjectThatImplementsIStorable = новый MyStorable();
...//остальная часть вашего кода здесь.
Затем х нельзя поместить в хранилище, но его можно перенести в хранилище. Следующие действия будут работать: (Сохранить) x
Обратите внимание, что хотя MyStorable реализует IStorable, между Store и Store нет никакой связи. Это два разных класса, которые не происходят друг от друга.
и.
Ответ 5
T должен быть типом Store, избегающим использования typeof (Store