2012-06-27 10 views
5

estoy creando instancias de un tipo genérico utilizando la reflexión:fundición casos de tipo genérico creado usando Reflexión

public interface IModelBuilder<TModel> 
{ 
    TModel BuildModel(); 
} 

public class MyModel 
{ 
    public string Name { get; set; } 
} 

public class MyModelBuilder : IModelBuilder<MyModel> 
{ 
    public MyModel BuildModel() 
    { 
     throw new NotImplementedException(); 
    } 
} 

En tiempo de ejecución de todo lo que sabemos es el tipo de modelo, por ejemplo, MyModel. No puedo encontrar las instancias del constructor de modelos relevantes de este modo:

var modelBuilders = from t in Assembly.GetExecutingAssembly().GetTypes() 
       from i in t.GetInterfaces() 
       where i.IsGenericType 
         && i.GetGenericTypeDefinition() == typeof(IModelBuilder<>) 
         && i.GetGenericArguments()[0] == modelType 
       select t; 

var builder = Activator.CreateInstance(modelBuilders.First()); 

Pero no estoy seguro de cómo puedo entonces emitir el ejemplo como IModelBuilder<TModel> para que pueda llamar y trabajar con el resultado de BuildModel().

Respuesta

14

Dado que modelType es solo una instancia de Type, no puede hacerlo automáticamente, ya que no hay ninguna API no genérica disponible. Varias opciones:

1: utilizar la reflexión, por ejemplo (no probado)

object builder = Activator.CreateInstance(...); 
var model=builder.GetType().GetMethod("BuildModel").Invoke(builder,null); 

2: tramposo con dynamic:

dynamic builder = Activator.CreateInstance(...); 
var model = builder.BuildModel(); 

3: hacer una versión no genérica de IModelBuilder, y el uso que

Tenga en cuenta que 1 & 2 dependen de una implementación pública del inte y fallará para una implementación explícita (perfectamente legal) de la interfaz. Por "1", se puede solucionar este problema a través de:

var model = typeof(IModelBuilder<>).MakeGenericType(modelType) 
     .GetMethod("BuildModel").Invoke(builder); 

Una opción final es astuto para voltear de un método no genérico en un método genérico, por lo que en el interior del método genérico que puede utilizar todos los miembros directamente. Hay una manera perezosa para hacer eso a través de dynamic:

interface ISneaky<T> 
{ 
    T Foo { get; } 
} 
class Sneaky<T> : ISneaky<T> 
{ 
    T ISneaky<T>.Foo { get { return default(T); } } 
} 
class Program 
{ 
    static void Main() 
    { 
     Execute(typeof(int)); 
    } 
    static void Execute(Type t) 
    { 
     dynamic obj = Activator.CreateInstance(
      typeof(Sneaky<>).MakeGenericType(t)); 
     // crafy hack to flip from non-generic code into generic code: 
     Evil(obj); 
    } 
    static void Evil<T>(ISneaky<T> sneaky) 
    { // in here, life is simple; no more reflection 
     Console.WriteLine("{0}: {1}", typeof(T).Name, sneaky.Foo); 
    } 
} 
+0

Tenía la sensación de que este sería el caso. Creo que la opción 2 funcionará mejor para mí, ya que necesito trabajar con propiedades en el modelo que se creó. –

+0

@Ben si necesita hacer una cantidad de trabajo no trivial, puede * también * hacer esto con un método genérico, que * invoque mediante reflejo *, es decir, en el método genérico de '' es 'IModelBuilder '. ¿Quieres un ejemplo de eso? –

+2

¡agradable! Y muy furtivo :) –

Cuestiones relacionadas