2012-02-04 1 views
28

У меня есть общий тип 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?

+0

Вы можете сделать 'obvious' причина более очевидно? Или просто скажите нам, что является причиной. Или вы на самом деле означает' 'TypeOf ... , не заменяя его вашим типом? – Snowbear

+1

@Snowbear, что часть кода не компилируется, потому что Generics не разрешают экземпляры типов использоваться как T. Я не знаю этого типа, это может быть любой объект, который реализует IStorable-интерфейс – Bazzz

+0

Какой тип отладчика считает x? Правильный ли он? – CarneyCode

ответ

27

Поскольку фактический тип 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 интерфейс, который не использует дженерики, и использует 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); 
+0

Я боялся, что это спустится. Это эмпирическое правило, что, когда я знаю фактический тип T только через отражение, что весь доступ к реальному объекту должен также проходить через отражение? – Bazzz

+0

@Bazzz Да, если единственное, что вы знаете о 'someObjectThatImplementsIStorable', это то, что это« объект », реализующий« Истационал », мало что можно сделать, чтобы избежать отражения. Если вы получали 'someObjectThatImplementsIStorable' как универсальный тип' T', вы также могли бы наложить на 'Store '. Альтернативой было бы избежать генериков в подмножестве методов (я скоро обновлю ответ, чтобы показать вам, как это сделать). – dasblinkenlight

+0

Я пойду так, я знаю, что могу сделать эту работу, и другие варианты, похоже, не доступны. Спасибо за ответ. Я хотел бы видеть подмножество, хотя, если у вас есть время. Просто из интереса и чему-то научиться. – Bazzz

3

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

что-то вроде

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] 
+0

интересно, но тогда? Как мне назвать этот метод? можете ли вы добавить строку образца кода? – Bazzz

+0

Да, пока вы называете его каким-либо объектом T, то есть Beer item = new Beer(); var beerStore = IConstructStore (item); beerStore.whatever() должен работать нормально –

+0

'typeof (Store )' не компилируется, также не используется 'as Store ' – Bazzz

2

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

Вы можете попробовать ввести интерфейс 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)); 
+0

Благодарим за предложение, на самом деле у меня есть входные и выходные родовые типы в магазине , так что путь не работает. Что касается другого предложения, помимо экземпляра objectThatImplementsIStorable, я не знаю фактического типа где угодно. Я думаю, что в итоге я воспользуюсь Reflection на объекте, созданным Activator. +1 для хорошего объяснения, почему проблема существует. – Bazzz

0

Предположим, что someObjectThatImplementsIStorable имеет тип MyStorable.

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

Затем х нельзя отнести в магазин, но его можно отбросить в Магазин. Будет выполнено следующее: (Хранить) x

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

u.

+0

Вам удалось описать проблему другими словами. :) – Bazzz

+0

Вы не можете использовать общий тип без включения T, т.е. (Store) x не компилируется –

0

T должен быть типом магазина избегая использования TYPEOF (магазин

Смежные вопросы