Kann ich zur Laufzeit eine .NET-Assembly laden und einen Typ instanziieren, der nur den Namen kennt?

178

Ist es möglich, ein Objekt zur Laufzeit zu instanziieren, wenn ich nur den DLL-Namen und den Klassennamen habe, ohne einen Verweis auf die Assembly im Projekt hinzuzufügen? Die Klasse implementiert eine Schnittstelle. Sobald ich die Klasse instanziiert habe, werde ich sie in die Schnittstelle umwandeln.

Baugruppenname:

library.dll

Modellname:

Company.Project.Classname


EDIT: Ich habe nicht den absoluten Pfad der DLL, Assembly.LoadFilewird also nicht funktionieren. Die DLL befindet sich möglicherweise im Anwendungsstamm, im System32 oder sogar im GAC.

Megabyte
quelle

Antworten:

221

Ja. Sie müssen verwenden Assembly.LoadFrom, um die Assembly in den Speicher zu laden, und dann können Sie Activator.CreateInstanceeine Instanz Ihres bevorzugten Typs erstellen. Sie müssen den Typ zuerst mithilfe der Reflexion nachschlagen. Hier ist ein einfaches Beispiel:

Assembly assembly = Assembly.LoadFrom("MyNice.dll");

Type type = assembly.GetType("MyType");

object instanceOfMyType = Activator.CreateInstance(type);

Aktualisieren

Wenn Sie den Namen der Assembly-Datei und den Typnamen haben, können Sie Activator.CreateInstance(assemblyName, typeName)die .NET-Typauflösung anfordern, um diese in einen Typ aufzulösen. Sie können dies mit einem try / catch abschließen, sodass Sie bei einem Fehlschlag eine Suche in Verzeichnissen durchführen können, in der Sie möglicherweise zusätzliche Assemblys speichern, die ansonsten möglicherweise nicht durchsucht werden. Dies würde zu diesem Zeitpunkt die vorhergehende Methode verwenden.

Jeff Yates
quelle
2
Ich habe nicht den absoluten Pfad der DLL, also Assemlby.LoadFile ect. wird nicht funktionieren, irgendwelche anderen Ideen?
MegaByte
@rp Immer gerne helfen (und nur ein Jahr zu spät!)
Jeff Yates
2
@ MegaByte: LoadFrom unterscheidet sich von LoadFile. Es löst Ihre Abhängigkeiten auf und sollte den DLL-Namen aus bekannten Pfaden (GAC, exe-Verzeichnis usw.) auflösen. Weitere Informationen finden Sie unter MSDN.
Jeff Yates
1
Noch etwas ... (ich wieder) Ähm, Sie können nicht einfach "MyType" als Typnamen haben, es muss NAMESPACE folgen. Das wäre also genauer:Type type = assembly.GetType("MyNamespace"+"."+"MyType");
Cipi
1
@Cipi: Technisch gesehen ist ein Typ ein vollständiger Name mit Namespace (das Konzept des Namespace ist eine praktische Annehmlichkeit). Sie können einen Typ ohne Namespace in der CLR haben - ich habe nur ein stark vereinfachtes Beispiel bereitgestellt.
Jeff Yates
36

Berücksichtigen Sie die Einschränkungen der verschiedenen Load*Methoden. Aus den MSDN- Dokumenten ...

LoadFile lädt keine Dateien in den LoadFrom-Kontext und löst Abhängigkeiten nicht über den Ladepfad auf, wie dies bei der LoadFrom-Methode der Fall ist.

Weitere Informationen zu Ladekontexten finden Sie in den LoadFromDokumenten.

Anthony Mastrean
quelle
19

Activator.CreateInstance sollte funktionieren.

IFace object = (IFace)Activator.CreateInstance( "AssemblyName",
                                                "TypeName" )
                               .Unwrap();

Hinweis: Der Typname muss der vollständig qualifizierte Typ sein.

Beispiel:

var aray = (IList)Activator.CreateInstance("mscorlib","System.Collections.ArrayList").Unwrap();
aray.Add(10);

foreach (object obj in aray)
{
    Console.WriteLine(obj);
}
Tvanfosson
quelle
1
Nur ein Hinweis dazu: TypeNamemuss voll qualifiziert sein. Ich musste das so nennen: Activator.CreateInstance("MyAssembly","MyAssembly.TypeName") Und das gibt eine zurück ObjectHandle. Um zu Ihrer Benutzeroberfläche zu gelangen, müssen Sie ObjectHandle.UnWrap()
Folgendes
7

Ich fand diese Frage und einige Antworten sehr nützlich, hatte jedoch Pfadprobleme, sodass diese Antwort das Laden der Bibliothek durch Auffinden des Bin-Verzeichnispfads abdeckte.

Erste Lösung:

string assemblyName = "library.dll";
string assemblyPath = HttpContext.Current.Server.MapPath("~/bin/" + assemblyName);
Assembly assembly = Assembly.LoadFrom(assemblyPath);
Type T = assembly.GetType("Company.Project.Classname");
Company.Project.Classname instance = (Company.Project.Classname) Activator.CreateInstance(T);

Zweite Lösung

string assemblyName = "library.dll";
string assemblyPath = HttpContext.Current.Server.MapPath("~/bin/" + assemblyName);
Assembly assembly = Assembly.LoadFile(assemblyPath);
(Company.Project.Classname) instance = (Company.Project.Classname) assembly.CreateInstance("Company.Project.Classname");

Sie können dasselbe Prinzip für Schnittstellen verwenden (Sie würden eine Klasse erstellen, aber in eine Schnittstelle umwandeln), z.

(Company.Project.Interfacename) instance = (Company.Project.Interfacename) assembly.CreateInstance("Company.Project.Classname");

Dieses Beispiel gilt für Webanwendungen, ähnliche können jedoch auch für Desktopanwendungen verwendet werden. Nur der Pfad wird beispielsweise auf unterschiedliche Weise aufgelöst

Path.GetDirectoryName(Application.ExecutablePath)
Sofija
quelle
5

Es ist einfach.

Beispiel aus MSDN:

public static void Main()
{
    // Use the file name to load the assembly into the current
    // application domain.
    Assembly a = Assembly.Load("example");
    // Get the type to use.
    Type myType = a.GetType("Example");
    // Get the method to call.
    MethodInfo myMethod = myType.GetMethod("MethodA");
    // Create an instance.
    object obj = Activator.CreateInstance(myType);
    // Execute the method.
    myMethod.Invoke(obj, null);
}

Hier ist ein Referenzlink

https://msdn.microsoft.com/en-us/library/25y1ya39.aspx

user3722131
quelle
Das ist eine schreckliche Möglichkeit, das dynamische Laden von Code zu unterstützen. MS hat uns immer gerne gezwungen, auf zu viele Details einzugehen.
Klarer
3

Ab Framework v4.5 können Sie Activator.CreateInstanceFrom () verwenden , um Klassen in Assemblys einfach zu instanziieren. Das folgende Beispiel zeigt, wie es verwendet wird und wie eine Methode aufgerufen wird, die Parameter übergibt und den Rückgabewert abruft.

    // Assuming moduleFileName contains full or valid relative path to assembly    
    var moduleInstance = Activator.CreateInstanceFrom(moduleFileName, "MyNamespace.MyClass");
    MethodInfo mi = moduleInstance.Unwrap().GetType().GetMethod("MyMethod");
    // Assuming the method returns a boolean and accepts a single string parameter
    bool rc = Convert.ToBoolean(mi.Invoke(moduleInstance.Unwrap(), new object[] { "MyParamValue" } ));
afiorillo
quelle
2
((ISomeInterface)Activator.CreateInstance(Assembly.LoadFile("somePath").GetTypes()[0])).SomeInterfaceMethod();
abatishchev
quelle
2

Sie können eine Assembly mit den Methoden * Assembly.Load ** laden. Mit Activator.CreateInstance können Sie neue Instanzen des gewünschten Typs erstellen. Beachten Sie, dass Sie den vollständigen Typnamen der Klasse verwenden müssen, die Sie laden möchten (z. B. Namespace.SubNamespace.ClassName ). Verwenden der Methode InvokeMember des Typs Klasse können Sie Methoden für den Typ aufrufen.

Berücksichtigen Sie außerdem, dass eine Assembly nach dem Laden erst entladen werden kann, wenn auch die gesamte AppDomain entladen wurde (dies ist im Grunde ein Speicherverlust).

Dario Solera
quelle
2

Je nachdem, wie wichtig diese Art von Funktionalität für Ihr Projekt ist, sollten Sie etwas wie MEF in Betracht ziehen, das das Laden und Zusammenbinden von Komponenten für Sie übernimmt.

Kent Boogaart
quelle
2
Assembly assembly = Assembly.LoadFrom("MyAssembly.dll");

Type type = assembly.GetType("MyType");

dynamic instanceOfMyType = Activator.CreateInstance(type);

Auf diese Weise können Sie Funktionen verwenden, die nicht dazu dienen, methodinfo abzurufen und dann aufzurufen. Diese Instanz von MyType.MethodName () gefällt Ihnen. Sie können Intellisense jedoch nicht verwenden, da dynamische Typen zur Laufzeit und nicht zur Kompilierungszeit eingegeben werden.

David Mkheyan
quelle
1

Ja, Sie möchten die statische Load-Methode für die Assembly-Klasse verwenden und dann die CreateInstance-Methode für die Assembly-Instanz aufrufen, die Sie vom Aufruf von Load erhalten haben.

Sie können auch eine der anderen statischen Methoden aufrufen, beginnend mit "Laden" für die Assembly-Klasse, abhängig von Ihren Anforderungen.

casperOne
quelle
0

Sie können diese Dinge auf folgende Weise tun:

using System.Reflection;

Assembly MyDALL = Assembly.Load("DALL"); //DALL name of your assembly
Type MyLoadClass = MyDALL.GetType("DALL.LoadClass"); // name of your class
 object  obj = Activator.CreateInstance(MyLoadClass);
Pankaj
quelle