2008-11-17 4 views
15

Ich habe eine Reihe von Klassen, jeder ist ein anderer strategy, um die gleiche Arbeit zu tun.Erstellen Sie ein Objekt, das nur den Klassennamen kennt?

namespace BigCorp.SuperApp 
{ 
    public class BaseClass { } 
    public class ClassA : BaseClass { } 
    public class ClassB : BaseClass { } 
} 

Die Auswahl der zu verwendenden Strategie ist konfigurierbar. Ich möchte nur den Klassennamen 'ClassB' anstelle des vollständigen Typenamens 'BigCorp.SuperApp.ClassB' in der app.config-Datei konfigurieren.

<appConfig> 
    <SuperAppConfig> 
     <Handler name="ClassB" /> 
    </SuperAppConfig> 
</appConfig> 

Allerdings scheitern die Reflexions Anrufe, weil sie die vollständigen Typnamen erwarten, vor allem

Type t = Type.GetType("ClassB"); // results in t == null 
BaseClass c = Activator.CreateInstance(t) as BaseClass; // fails 

Wie kann ich dies funktioniert, während nur die Klassennamen zu konfigurieren? Verketten Sie den Namespace mit dem Klassennamen für den vollständigen Typnamen? Gibt es noch einen weiteren Anruf, der funktioniert?

Wenn Sie denken, dass dies nutzlos ist und ich erwarten sollte, dass die Konfiguration den vollständigen Typnamen enthält, bin ich offen für diese Lösung! Begründe mich einfach, um mich zu überzeugen.

(Ich werde von außerhalb dieser Montage/Namensraum nicht eine Art werden geladen)

+0

Ich könnte einfach einen IoC-Container verwenden, mit langen Namen umgehen und die Erstellung von Objekten für mich erledigen lassen! –

Antwort

6

Da Sie alle Klassen wissen aus dem gleichen Namensraum kommen, konfigurieren Sie es einmal und verwenden, die:

<appConfig> 
    <SuperAppConfig handlerNamespace="BigCorp.SuperApp"> 
     <Handler class="ClassB" /> 
    </SuperAppConfig> 
</appConfig> 

Edit: Ich änderte Name-Klasse besser die Bedeutung zu bezeichnen dieses Attribut.

+0

Ich schätze die Antworten, Code und Diskussion über das Laden von Baugruppen. Aber ich mag Bryans Antwort für den Fokus auf Konfiguration (da ich nicht vom vollen Typnamen wegkommen kann). –

18

Entweder die Assembly qualifizierten Namen verwenden, oder halten die Versammlung und Assembly.GetType(name) verwenden bekommen. In diesem Fall, da Sie die Typen in der Konfigurationsdatei möchten, Assembly qualifizierten ist ein gültiger Weg zu gehen - aber da Sie alle Ihre Arten kennen, sind in der gleichen Assembly:

Assembly assembly = typeof(SomeKnownType).Assembly; // in the same assembly! 
Type type = assembly.GetType(name); // full name - i.e. with namespace (perhaps concatenate) 
object obj = Activator.CreateInstance(type); 

Die statische Type.GetType(string) hat Sondieren Regeln das führt oft zu Verwirrung ... es sieht auf die aufrufende Assembly und einige System-Assemblys - aber nicht alle geladenen Assemblys.

+0

Ich habe eine Service-Referenz, die ich mit diesem Aufruf aufrufen möchte, aber es wird nicht in der Typenliste der Baugruppe (aus der Klasse) angezeigt. Obwohl ich ein Objekt der Referenz erstellen kann, indem ich den Konstruktor hardcode aufruft. – MrFox

5

Aufgrund der obigen Zeile ist es sicher anzunehmen, dass Sie wissen, was der Namespace ist. Könnten Sie nicht so etwas wie:

Type t = Type.GetType("Namespace." + className); 
BaseClass c = Activator.CreateInstance(t) as BaseClass; 

Wenn Sie möglicherweise erwarten, dass zusätzliche Strategie Klassen hinzufügen in Zukunft geladen werden, vielleicht über eine zusätzliche Montage, müssten Sie vollständig Ihre Klassennamen qualifizieren. Dies wird trotzdem empfohlen, da Sie eine erweiterte Erweiterbarkeit für Ihre Anwendung bereitstellen können.

+0

Das hängt tatsächlich davon ab, wo dieser Code ist (und wie du "von außen" interpretierst - d. H. Ist das die Klasse? Oder der Aufrufer?). Ohne einen für die Assemblierung qualifizierten Namen überprüft Type.GetType (String) * nur die aktuelle Assembly und einige Systemassemblys. Es findet keine Typen in zufällig referenzierten DLLs. –

2

Ich gehe mit dem vollständigen Typ Name in der Anwendungskonfiguration.Nachfolgend finden Sie eine etwas umfangreichere, aber immer noch triviales Beispiel

<SuperAppConfig> 
    <ObjectConfig provider="BigCorp.SuperApp.ClassA"> 
     <add name="one" /> 
     <add name="two" /> 
    </ObjectConfig> 
</SuperAppConfig> 

Und die Factory-Klasse, die dies tatsächlich schafft

private static Assembly a = typeof(IFactoryObject).Assembly; 
public static IFactoryObject CreateObject(String providerName) 
{ 
    Type t = a.GetType(providerName) 
    IFactoryObject o = Activator.CreateInstance(t) as IFactoryObject; 
    return o; 
} 
1
BaseClass c = Activator.CreateInstance(t) as BaseClass; // fails 

auch aus der Tatsache ergeben könnten, dass CreateInstance keine Instanz zurückgibt von BaseClass statt einer Instanz von BaseClass, die in ein ObjectHandle eingebettet ist.

In Ihre BaseClass konvertieren, nachdem Sie die UnWrap-Methode verwendet haben.