Créer un type générique avec une interface générique au moment de l'exécution

.net c# expression-trees generics linq

Question

Je travaille sur un problème depuis quelques heures maintenant et je pense que je suis proche. Je travaille sur une application où nous pourrions avoir 50 à 100 types qui fonctionnent de la même manière. Donc au lieu de créer 50-100 classes, j'ai essayé de le rendre générique et voici ce que j'ai:

C'est la classe de base:

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

Et voici l'interface:

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

Et voici comment je l'utilise:

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

Je suis coincé sur cette ligne d'en haut:

var ravenWriter = Activator.CreateInstance(ravenWriterType);

Voici ma question:
Comment puis-je utiliser cela comme RavenWriterBase ou IRavenWriter? Quelque chose comme:

ravenWriter.Initialize(60, func);

Je pense que cela doit être quelque chose comme ça, mais je dois spécifier un type pour IRavenWriter <> et je ne le connais pas encore:

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

Si je survole ravenWriter, mon objet me parvient:

entrez la description de l'image ici

Mais maintenant, je dois pouvoir l'utiliser de manière générique. Comment puis je faire ça?

Mettre à jour:

Je viens de penser à utiliser le mot clé dynamique, et cela fonctionne:

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

J'ai triché un peu parce que je me suis rendu compte que le Func était le même pour chaque IDataEntity, il n'était donc pas nécessaire de passer en paramètre à Initialize (). Cependant, au moins maintenant je peux appeler Initialize (). Mais maintenant que le Func est le même, je n’aurais plus besoin de l’interface générique.

Réponse acceptée

Ma solution serait de:

  • Créer une interface non générique d' IRavenWriter
  • Faire en IRavenWriter<T> hérite de IRavenWriter
  • Conserver Execute et ExecutionIntervalInSeconds dans IRavenWriter
  • Assurez- IRavenWriter avoir Func<DateTime> et l'utiliser dans votre écrivain
  • Déplacer l' Initialize vers IRavenWriter<T>
  • Utilisez une fabrique pour initialiser le Func en fonction du type et de l'expression:

Par exemple:

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

Et vous utilisez:

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

Réponse populaire

Il n’est pas difficile de transformer Type exécution en paramètre Type générique au moment de la compilation. Introduisez simplement une nouvelle interface pour créer / initialiser vos objets:


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

Créez maintenant RawenWriterFactory avec dataEntityType comme vous avez créé ravenWriter et utilisez-le via une interface non générique IRavenWriterFactory.

Cependant, il pourrait y avoir des solutions plus simples si vous modifiez votre conception Par exemple, si vous transformez la méthode Initialize en constructeur, vous pourrez passer func comme paramètre Activator.CreateInstance et vous n’auriez pas besoin d’une interface générique pour l’initialisation.




Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi