2012-04-13 22 views
5

Ich habe ein Problem für ein paar Stunden bearbeitet, und ich denke, ich bin nah dran. Ich arbeite an einer App, wo wir 50-100 Typen haben könnten, die genauso funktionieren. Anstatt also 50-100 Klassen zu schaffen, versuchte ich es generisch zu machen und das ist, was ich habe:Create Generic Type mit Generic Interface zur Laufzeit

Dies ist die Basisklasse:

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

Und das ist die Schnittstelle:

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

Und das ist, wie ich es bin mit:

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

ich bin auf dieser Linie von oben stecken:

var ravenWriter = Activator.CreateInstance(ravenWriterType); 

Diese meine Frage ist:
Wie kann ich das als RavenWriterBase oder IRavenWriter verwenden? Etwas wie:

ravenWriter.Initialize(60, func); 

Ich denke, es ist so etwas wie dies sein muss, aber ich brauche einen Typ für < IRavenWriter angeben> und ich weiß es noch nicht:

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

Wenn ich schweben über ravenWriter, ich habe erfolgreich mein Objekt:

enter image description here

Aber jetzt muss ich in der Lage es in allgemeiner Weise zu verwenden. Wie kann ich das machen?

Update:

Ich dachte nur an das dynamische Keyword verwendet wird, und das funktioniert:

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

ich ein wenig betrogen, weil ich erkannte, dass die Func das gleiche für jeden IDataEntity war, so das war nicht notwendig, um als Parameter an Initialize() übergeben zu werden. Allerdings kann ich jetzt zumindest Initialize aufrufen(). Aber jetzt, wo der Func derselbe ist, sollte ich auch nicht die generische Schnittstelle brauchen.

+2

Klingt wie unsachgemäße Verwendung von Generika. Wenn Sie feststellen, dass Sie verschiedene Methoden auf unterschiedlichen Typen ausführen müssen, die Typen jedoch nur zur Laufzeit bekannt sind, möchten Sie Polymorphie verwenden. Was bringt Ihnen die Generika? Können Sie die Methoden so wie sie sind ohne Generics ausführen, d. H. Mit IDataEntity? – mellamokb

+1

stimme ich mit mellamokb überein. Wenn Sie jedoch auf Generics festgelegt sind, warum erstellen Sie nicht eine Wrapper-Klasse, die Sie non-genarisch instanziieren können. Der Wrapper sollte über eine Methode verfügen, die je nach dem als Argument übergebenen Typ eine Instanz von RavenWriterBase zurückgibt. Instanziieren Sie dann den Wrapper und rufen Sie die Methode mit dem angeforderten Datentyp als Parameter auf. Dies erfordert eine große switch-Anweisung, aber mindestens 200 separate Klassen. – dcow

+2

Erstellen Sie andere Ausdrücke, die IDataEntity für T nicht verwenden? Oder ist T immer ein IDataEntity? Und wenn T immer IDataEntity ist, warum sollten Sie Generika verwenden? –

Antwort

2

würde meine Lösung sein:

  • eine nicht-generic Erstellen Schnittstelle von IRavenWriter
  • Make IRavenWriter<T> vererben IRavenWriter
  • Halten Execute und ExecutionIntervalInSeconds in IRavenWriter
  • Make IRavenWriter haben Func<DateTime> und Verwendung dass in Ihrem Schreiber
  • M ove Initialize-IRavenWriter<T>
  • eine Fabrik Verwenden Sie die Func nach der Art und Ausdruck zu initialisieren:

Zum Beispiel:

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

Und Sie verwenden:

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

Ich bin mir nicht sicher, ob ich folge. Wie kann ich ravenWriter als RavenWriterBase <> oder IRavenWriter <> verwenden? –

+0

@BobHorn es nicht tut. Sie können dort keine Kenntnis von 'RavenWriterBase <>' haben. Stattdessen müssen Sie 'IRavenWriterBase' (nicht-generisch) dort verwenden.Kompilierzeitwissen des Typs existiert dort nicht, so dass Sie nicht casten können. – Aliostad

+0

Danke. Ich habe gerade meine Frage bearbeitet, um zu zeigen, wie ich Initialize() mit dynamic aufrufen kann. Ich denke, das sollte funktionieren. –

1

Es ist nicht wirklich schwer, Lauf-Zeit Type in Compile-Time Generic Typ Parameter zu drehen. Nur neue Schnittstelle einführen für die Erstellung/Initialisieren Ihre Objekte:

 

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

Jetzt erstellen Sie einfach RawenWriterFactory mit dataEntityType wie Sie ravenWriter erstellt haben und es über nicht-generische IRavenWriterFactory Schnittstelle verwenden.

Allerdings könnte es einfachere Lösungen geben, wenn Sie Ihr Design ändern. Z.B. Wenn Sie die Initialize-Methode in Konstruktor umwandeln, können Sie func als Activator.CreateInstance-Parameter übergeben, und Sie müssen keine generische Schnittstelle für die Initialisierung verwenden.