2012-02-04 21 views
28

Tengo un tipo genérico Store<T> y uso Activator para hacer una instancia de este tipo. Ahora, ¿cómo, después de usar el Activador, puedo convertir el objeto resultante del tipo object al tipo instanciado? Sé el tipo que utilicé para crear una instancia del genérico. Por favor, vea el siguiente código:Cómo usar Activator para crear una instancia de un Tipo genérico y devolverlo a ese tipo?

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

Lo que me gustaría hacer es algo como esto:

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

pero eso no funciona por razones obvias. Como alternativa Traté:

var store = (Store<IStorable>)x; 

que podría posiblemente trabajar en mi opinión, pero da una InvalidCastException.

¿Cómo obtengo acceso de nuevo a los métodos Store<T> que sé que están en el objeto x?

+0

¿Se puede hacer 'obvious' razón más obvia? O sólo nos dicen cuál es la razón. ¿O es que realmente quiere decir' 'typeof ... , no sustituyéndolo por tu tipo? – Snowbear

+1

@Snowbear ese fragmento de código no se compila porque los genéricos no permiten que las instancias de tipos se usen como T. No conozco ese tipo, podría ser cualquier objeto que implemente el Interfaz IStorable – Bazzz

+0

¿Qué tipo de depurador cree que es x? ¿Es el tipo correcto? – CarneyCode

Respuesta

27

Dado que el tipo real T está disponible para su uso sólo a través de la reflexión, que se necesitan para tener acceso a métodos de Store<T> través de la reflexión, así:

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

EDITAR También puede definir un IStore interfaz adicional que no usa los genéricos, y utiliza IStorable lugar:

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

Su Store<T> quedaría genérico, pero se podrían obtener acceso a su CountItems echando a IStore:

var x = (IStore)Activator.CreateInstance(constructedType, new object[] { someParameter }); 
var count = x.CountItems((IStorable)someObjectThatImplementsStorable); 
+0

Temía que todo se redujera a esto. ¿Es una regla empírica que cuando conozco el tipo real T solo a través de la reflexión, todo acceso al objeto real necesita pasar por la reflexión también? – Bazzz

+0

@Bazzz Sí, si lo único que sabes sobre 'someObjectThatImplementsIStorable' es que se trata de un 'objeto' que implementa 'IStorable', hay poco que puedes hacer para evitar la reflexión. Sin embargo, si estuviera obteniendo 'someObjectThatImplementsIStorable' como un tipo genérico' T', también podría convertir 'Store '. Una alternativa sería evitar genéricos en un subconjunto de métodos (actualizaré la respuesta en breve para mostrarle cómo). – dasblinkenlight

+0

Iré de esta manera, sé que puedo hacer que esto funcione, y otras opciones no parecen estar disponibles. Gracias por la respuesta. Sin embargo, me gustaría ver el subconjunto, si tienes tiempo. Solo por interés y para aprender algo. – Bazzz

3

¿Puedes simplemente envolverlo?

algo así como

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

o me estoy perdiendo lo que está tratando de hacer?

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

impresiones

1 
ConsoleApp1.Store'1[ConsoleApp1.Beer] 
+0

interesante, ¿pero entonces? ¿Cómo llamo a este método? ¿Puedes agregar una línea de código de muestra? – Bazzz

+0

sí, siempre que lo llame con cualquier objeto T se ingiere, es decir, artículo de Cerveza = cerveza nueva(); var beerStore = IConstructStore (artículo); beerStore.whatever() debería funcionar bien –

+0

'typeof (Tienda )' no se compila, y '' Store ' – Bazzz

2

La respuesta más adecuada en mi opinión sería 'no puedes hacerlo de esta manera'.

Puede tratar de introducir una interfaz IStorage e intentar que sea covariante o contravariante (¿ha visto esa opción?). Si no es una opción, por ejemplo, si tiene los tipos genéricos de entrada y salida utilizados en Storage, entonces no hay forma de implementar lo que desea.La razón es que Storage<Beer> no se pueden utilizar con seguridad como Storage<IStorable> debido a este caso:

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

La única solución posible para usted como lo veo es mover echar fuera de este método y convertir el objeto en el lugar donde se sabe todos los tipos exactos:

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

Gracias por su sugerencia, de hecho tengo tipos genéricos de entrada y salida en la tienda , por lo que la ruta de acceso no funciona. En cuanto a la otra sugerencia, además de una instancia del objetoThatImplementosIStorable, no sé el Tipo real en cualquier lugar. Creo que terminaré usando Reflection en el objeto que creó el Activador. +1 para una buena explicación de por qué existe el problema. – Bazzz

0

Digamos que someObjectThatImplementsIStorable es del tipo MyStorable.

p. Ej. MyStorable someObjectThatImplementsIStorable = new MyStorable(); ... // resto de su código aquí.

Entonces x no se puede convertir a la tienda, pero se puede convertir a la tienda. Lo siguiente funcionará: (Tienda) x

Tenga en cuenta que aunque MyStorable implementa IStorable, no hay relación entre Store y Store. Estas son dos clases distintas que no se derivan una de la otra.

u.

+0

Ha logrado describir el problema en otras palabras. :) – Bazzz

+0

No puede lanzar un tipo genérico sin incluir la T, es decir, (tienda) x no compilará –

0

T debe ser el tipo de tienda evitando el uso de typeof (tienda

Cuestiones relacionadas