2009-03-26 10 views
15

Ich habe eine generische Klasse wie folgt aus:Wie erstelle ich eine generische Klasse aus einer Zeichenfolge in C#?

public class Repository<T> {...} 

Und ich Instanz müssen, dass mit einem String ... Beispiel:

string _sample = "TypeRepository"; 
var _rep = new Repository<sample>(); 

Wie kann ich das tun? Ist das überhaupt möglich?

Danke!

+0

Beachten Sie, dass Sie in Ihrem Programm nicht auf Repository verweisen können, so dass der Typ von _rep "Objekt" oder eine gemeinsame Schnittstelle für alle Repository sein muss. –

Antwort

24

Hier ist meine 2 Cent:

Type genericType = typeof(Repository<>); 
Type[] typeArgs = { Type.GetType("TypeRepository") }; 
Type repositoryType = genericType.MakeGenericType(typeArgs); 

object repository = Activator.CreateInstance(repositoryType); 

die Frage in Kommentar Antworten.

MethodInfo genericMethod = repositoryType.GetMethod("GetMeSomething"); 
MethidInfo closedMethod = genericMethod.MakeGenericMethod(typeof(Something)); 
closedMethod.Invoke(repository, new[] { "Query String" }); 
+0

Und wie kann ich auf die Repository-Methoden zugreifen? – Paul

+2

Auf dieselbe Art und Weise haben Sie das Repository für den Typ erstellt, der nur in der Laufzeit bekannt ist und Reflektionen verwendet. Sie wollen Flexibilität, Sie müssen dafür bezahlen. – alex

+0

Können Sie mir zeigen, wie das geht? Danke! – Paul

-3

Das "T" in einer generischen Klasse steht für einen Typ, nicht für eine Instanz eines Typs. Also, wenn Sie Ihr Repository wollen String-Objekte zu halten, verwenden Sie dann

var _rep = new Repository<string>(); 
+0

Ich denke, Sie haben die Frage falsch verstanden - die Zeichenfolge enthält einen Typnamen. –

16

zunächst den Typ Objekt erhalten mit Type.GetType(stringContainingTheGenericTypeArgument)

Dann typeof(Repository<>).MakeGenericType(theTypeObject) verwenden, um einen generischen Typ zu erhalten.

Und schließlich verwenden Activator.CreateInstance

+0

Ist nicht die eigentliche Frage "sollte er"? –

+0

von String zu Generiktyp? Vielleicht. MakeGenericType wird jedoch häufig in Linq-Providern und anderen Code, der den Ausdrucksbaum verwendet, verwendet.Es ist ein großartiger Helfer, obwohl es für jeden Tag an LOB-Apps natürlich keine Sorge ist. –

0

Das ist "Art" möglich. Irgendwie in dem du es kannst, aber es ist ein bisschen wie ein Hack.

Sie haben 3 Optionen:

Wenn Sie Ihre Klassen vorne wissen, eine switch-Anweisung verwenden. Im Grunde wie folgt aus:

switch(str){ 
    case "TypeRepository": return new Repository<TypeRepository>; 
} 

Als erweiterte Form der oben genannten, können Sie ein Wörterbuch anstelle eines Hash-Tabelle

var factory = new Dictionary<string, Func<object>>(); 
factory.Add("TypeRepository",() => new Repository<TypeRepository>()); 

var theObject = factory["TypeRepository"]() as Repository<TypeRepository>; 

Für die größte Flexibilität verwenden, können Sie verwenden Reflexion Strings Klassen entsprechen zur Laufzeit. Seien Sie sich bewusst, dass die Reflexion ziemlich langsam ist. Wenn Sie dies mit einer gewissen Regelmäßigkeit tun, möchten Sie es vermeiden. Als Beispiel, hier ist eine Frans Bouma's Methode. Ändern Sie einfach List<> zu Repository<> im Code:

public static object MakeList(string typeStr) 
{ 
    var nameSpace = "MyTestApplication" + "."; 

    var objType = Type.GetType(nameSpace + typeStr); // NAMESPACE IS REQUIRED 
    var listType = typeof(List<>).MakeGenericType(objType); 
    return Activator.CreateInstance(listType); 
} 

var listOfThings = MakeList("MyCoolObject") as List<MyCoolObject>; 

Hinweis: Es gibt keine Möglichkeit, die Tatsache zu vermeiden, dass alle diese Mechanismen object zurückkehren, die Sie dann zu Ihrem richtigen Typ haben zu werfen, anstatt nur Zurückkehren stark typisierte Zurückkehren Werte.

Dies ist unvermeidbar, weil Sie den Typ der Liste erst zur Laufzeit kennen und C# darauf baut, Dinge zur Kompilierzeit zu kennen (das meinen die Leute, wenn sie "statisch typisierte Programmiersprache" sagen). Es wird weniger schmerzhaft in C# 4 sein, wo du dynamic zurückgeben kannst, was dir das Casting erspart.

+0

Vorausgesetzt, dass der Typ mithilfe einer Zeichenfolge erstellt wird, wie würde C# einen bestimmten Typ sinnvoll mit dem Ergebnis verknüpfen? –

0

Generics arbeiten an Typen, nicht an Instanzen. Sie können mehr lesen here.

Klingt wie Sie nur einen Konstruktor Ihrer Klasse hinzufügen müssen, die in der gewünschten Art und nimmt initailizes den Wert:

public class Foo<T> 
{ 
    private T = default(T); 

    public Foo(T initValue) 
    { 
     _val = T; 
    } 
} 
2

Wenn ich Ihre Frage richtig verstanden habe ... Was Sie versuchen zu tun ist Nimm deinen Typ (Repository <T>) und konstruiere eine spezifische, generische Implementierung davon zur Laufzeit?

Wenn ja, werfen Sie einen Blick auf MakeGenericType. Sie können typeof (Repository) und den System.Type des Objekts, das Sie für T haben wollen, verwenden und so konstruieren. Sobald Sie den Typ haben, wird Activator.CreateInstance für Sie arbeiten.

1
Type repType = typeof(Repository <>).MakeGenericType(Type.GetType("System.String")); 
object rep = Assembly.GetAssembly(repType).CreateInstance(repType.FullName); 

Dies würde eine Instanz Repository<string> erstellen. Sie können "System.String" mit dem von Ihnen gewünschten Typ ersetzen.

2

Unter der Annahme, dass die Zeichenfolge den Namen einer Art hält, können Sie

object _rep = Activator.CreateInstance(typeof(Repository<>).MakeGenericType(Type.GetType(_sample))); 

jedoch schreiben, _rep wird eine nicht typisierte Objekt sein, und Sie werden keine Möglichkeit haben, etwas zu tun mit ihm. Da Sie zum Zeitpunkt der Kompilierung nicht wissen, welchen Typ die generische Klasse hat, gibt es keinen Typ, in den Sie sie umwandeln. (Es sei denn, das Repository erbt eine nicht-generische Klasse oder implementiert eine nicht-generische Schnittstelle)

Sie könnten das lösen, indem Sie eine nicht-generische Basisklasse für das Repository erstellen und das Objekt dorthin transformieren. Je nach Ihrer Situation möchten Sie wahrscheinlich Repository eine nicht-generische Klasse machen.

Wenn Repository eine Klasse ist, die andere Klassen enthält, ist es wahrscheinlich am besten, sie nicht generisch zu machen. Sie könnten den Konstruktor veranlassen, ein Type Objekt zu nehmen und dann Type.IsInstanceOfType für jedes hinzugefügte Objekt aufzurufen, um sicherzustellen, dass es der richtige Typ ist.

Wenn Repository eine kategorisierte Sammlung von Dingen ist, ist es wahrscheinlich am besten, sie nicht-generisch zu machen und ihren Konstruktor dazu zu bringen, eine string category zu nehmen.

Wenn Sie genauere Beratung wünschen, bitte posten Sie weitere Details über Ihre Situation.

+0

Mein Repository: public class Repository : RepositoryWithTypedId , IRepository {} public class RepositoryWithTypedId : IRepositoryWithTypedId Und ich kann nicht, dass implementantion ändern ... So ist es unmöglich? – Paul

+0

Es ist möglich, mit der Codezeile, die ich oben geschrieben habe, aber es wäre sehr schwierig, etwas damit zu tun. Warum versuchen Sie dies zu tun, und was machst du mit _rep? – SLaks

+0

Mein _rep ist ein DAO (Data Access Object) ... – Paul

3

Dies ist ein Job für das dynamic Schlüsselwort in C# 4.0.

Es gibt einige nette Hacks hier, die Ihnen eine Instanz Ihres Objekts, aber egal, was die aktuelle Version von C# nur wissen wird, hat es eine object, und es kann nicht viel mit einem Objekt selbst tun, weil Das aktuelle C# unterstützt keine späte Bindung. Sie müssen es zumindest in einen bekannten Typ umwandeln können, um irgendetwas damit zu tun. Letztendlich müssen Sie den benötigten Typ zur Kompilierzeit kennen oder Sie haben Pech.

Wenn Sie nun Ihre Repository-Klasse auf Typen beschränken können, die eine bekannte Schnittstelle implementieren, sind Sie in einer viel besseren Form.

+0

Also, ich habe keine Lösung für mein Problem ... – Paul

+0

Nein, und mein Punkt ist, dass Sie nicht eine, die wirklich funktioniert, bevor C# 4 kommt, wenn Sie Ihre Architektur neu denken können. –

+2

Wenn Sie teilen, was Sie mit diesen Repository-Objekten tun möchten, können wir Ihnen möglicherweise mit einem Design helfen, das besser funktioniert. –

Verwandte Themen