Crear tipo genérico con interfaz genérica en tiempo de ejecución

.net c# expression-trees generics linq

Pregunta

He estado trabajando en un problema durante un par de horas y creo que estoy cerca. Estoy trabajando en una aplicación donde podríamos tener 50-100 tipos que funcionan de la misma manera. Entonces, en lugar de crear 50-100 clases, traté de hacerlo genérico y esto es lo que tengo:

Esta es la clase base:

public class RavenWriterBase<T> : IRavenWriter<T> where T : class, IDataEntity

Y esta es la interfaz:

public interface IRavenWriter<T>
{
    int ExecutionIntervalInSeconds { get; }
    void Execute(object stateInfo);
    void Initialize(int executionIntervalInSeconds, Expression<Func<T, DateTime>> timeOrderByFunc);
}

Y así es como lo estoy usando:

private static void StartWriters()
{
    Assembly assembly = typeof(IDataEntity).Assembly;
    List<IDataEntity> dataEntities = ReflectionUtility.GetObjectsForAnInterface<IDataEntity>(assembly);

    foreach (IDataEntity dataEntity in dataEntities)
    {
        Type dataEntityType = dataEntity.GetType();
        Type ravenWriterType = typeof(RavenWriterBase<>).MakeGenericType(dataEntityType);

        Expression<Func<IDataEntity, DateTime>> func = x => x.CicReadTime;

        // This is where I'm stuck. How do I activate this as RavenWriterBase<T>?
        var ravenWriter = Activator.CreateInstance(ravenWriterType);

        //ravenWriter.Initialize(60, func);  // I can't do this until I cast.

        // More functionality here (not part of this issue)
    }
}

Estoy atrapado en esta línea desde arriba:

var ravenWriter = Activator.CreateInstance(ravenWriterType);

Esta es mi pregunta:
¿Cómo puedo usar eso como RavenWriterBase o IRavenWriter? Algo como:

ravenWriter.Initialize(60, func);

Creo que debe ser algo como esto, pero debo especificar un tipo para IRavenWriter <> y todavía no lo sé:

var ravenWriter = Activator.CreateInstance(ravenWriterType) as IRavenWriter<>;

Si muevo el cursor sobre ravenWriter, tengo mi objeto con éxito:

introduzca la descripción de la imagen aquí

Pero ahora necesito poder usarlo de una manera genérica. ¿Cómo puedo hacer eso?

Actualizar:

Acabo de pensar en usar la palabra clave dinámica, y esto funciona:

dynamic ravenWriter = Activator.CreateInstance(ravenWriterType);
ravenWriter.Initialize(60);

Hice trampa un poco porque me di cuenta de que el Func era el mismo para cada IDataEntity, por lo que no era necesario pasar como parámetro a Initialize (). Sin embargo, al menos ahora puedo llamar Inicializar (). Pero ahora que el Func es el mismo, tampoco debería necesitar la interfaz genérica.

Respuesta aceptada

Mi solución sería:

  • Crear una interfaz no genérica de IRavenWriter
  • Haga que IRavenWriter<T> herede de IRavenWriter
  • Mantenga Execute y ExecutionIntervalInSeconds en IRavenWriter
  • Haga que IRavenWriter tenga Func<DateTime> y use eso en su escritor
  • Mover Initialize a IRavenWriter<T>
  • Use una fábrica para inicializar la función según el tipo y la expresión:

Por ejemplo:

public class MyDateTime
{
    public DateTime This { get; set; }
}

public static Func<DateTime> GetFunk<T>(Expression<Func<T, DateTime>> timeOrderByFunc, T t)
{
    return () => timeOrderByFunc.Compile()(t);
}

Y usas:

GetFunk<MyDateTime>(x => x.This, new MyDateTime(){This = DateTime.Now});

Respuesta popular

No es realmente difícil convertir el Type tiempo de ejecución en un parámetro Tipo genérico de tiempo de compilación. Solo introduce una nueva interfaz para crear / inicializar tus objetos:


interface IRawenWriterFactory
{
  object Create();
}

class RawenWriterFactory<T> : IRawenWriterFactory
{
  public object Create()
  {
    Expression<Func<IDataEntity, DateTime>> func = x => x.CicReadTime;

    var ravenWriter = new RavenWriterBase<T>();
    ravenWriter.Initialize(60, func);

    return ravenWriter;
  }
}

Ahora simplemente cree RawenWriterFactory con dataEntityType tal como ha creado ravenWriter y utilícelo a través de la interfaz no genérica IRavenWriterFactory.

Sin embargo, podría haber soluciones más simples si cambia el diseño. Por ejemplo, si convierte el método de Inicialización en un constructor, podrá pasar la función como parámetro Activator.CreateInstance y no necesitará utilizar una interfaz genérica para la inicialización.



Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow