Crea un tipo generico con interfaccia generica in fase di esecuzione

.net c# expression-trees generics linq

Domanda

Ho lavorato a un problema per un paio d'ore e penso di essere vicino. Sto lavorando a un'app in cui potremmo avere 50-100 tipi che funzionano allo stesso modo. Quindi, invece di creare classi 50-100, ho provato a renderlo generico e questo è quello che ho:

Questa è la classe base:

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

E questa è l'interfaccia:

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

E questo è come lo sto 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)
    }
}

Sono bloccato su questa linea dall'alto:

var ravenWriter = Activator.CreateInstance(ravenWriterType);

Questa è la mia domanda:
Come posso usarlo come RavenWriterBase o IRavenWriter? Qualcosa di simile a:

ravenWriter.Initialize(60, func);

Penso che debba essere qualcosa del genere, ma ho bisogno di specificare un tipo per IRavenWriter <> e non lo conosco ancora:

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

Se passo il mouse sopra ravenWriter, ho successo con il mio oggetto:

inserisci la descrizione dell'immagine qui

Ma ora ho bisogno di poterlo usare in un modo generico. Come lo posso fare?

Aggiornare:

Ho solo pensato di utilizzare la parola chiave dinamica e questo funziona:

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

Ho barato un po 'perché ho capito che Func era lo stesso per ogni IDataEntity, quindi non era necessario passare come parametro a Initialize (). Tuttavia, almeno ora posso chiamare Initialize (). Ma ora che Func è lo stesso, non dovrei nemmeno aver bisogno dell'interfaccia generica.

Risposta accettata

La mia soluzione sarebbe:

  • Creare un'interfaccia non generica di IRavenWriter
  • Rendi IRavenWriter<T> ereditato da IRavenWriter
  • Mantieni Execute ed ExecutionIntervalInSeconds in IRavenWriter
  • Fai in modo che IRavenWriter abbia Func<DateTime> e lo usi nel tuo writer
  • Sposta Initialize su IRavenWriter<T>
  • Utilizzare una factory per inizializzare Func in base al tipo e all'espressione:

Per esempio:

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

E tu usi:

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

Risposta popolare

Non è davvero difficile girare run-time Type in fase di compilazione parametro generico tipo. Basta introdurre una nuova interfaccia per creare / inizializzare i tuoi oggetti:


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

Ora basta creare RawenWriterFactory con dataEntityType proprio come hai creato ravenWriter e usarlo tramite un'interfaccia IRavenWriterFactory non generica.

Tuttavia, potrebbero esserci soluzioni più semplici se cambierai il tuo design. Ad esempio, se si attiva il metodo Initialize in costruttore, sarà possibile passare func come parametro Activator.CreateInstance e non sarà necessario utilizzare l'interfaccia generica per l'inizializzazione.



Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché
Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché