Подтвердить что ты не робот

Как использовать 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?

4b9b3361

Ответ 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