Czy mogę załadować zestaw .NET w czasie wykonywania i utworzyć instancję typu znającego tylko nazwę?

178

Czy jest możliwe utworzenie instancji obiektu w czasie wykonywania, jeśli mam tylko nazwę DLL i nazwę klasy, bez dodawania odwołania do zestawu w projekcie? Klasa implementuje interfejs, więc kiedy utworzę instancję klasy, przerzucę ją na interfejs.

Nazwa zestawu:

library.dll

Wpisz imię:

Company.Project.Classname


EDYCJA: Nie mam absolutnej ścieżki do DLL, więc Assembly.LoadFilenie będzie działać. Biblioteka DLL może znajdować się w katalogu głównym aplikacji, system32 lub nawet załadowana do GAC.

MegaByte
źródło

Odpowiedzi:

221

Tak. Musisz użyć, Assembly.LoadFromaby załadować zestaw do pamięci, a następnie możesz użyć Activator.CreateInstancedo utworzenia wystąpienia preferowanego typu. Najpierw musisz sprawdzić typ za pomocą odbicia. Oto prosty przykład:

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

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

object instanceOfMyType = Activator.CreateInstance(type);

Aktualizacja

Gdy masz nazwę pliku zestawu i nazwę typu, możesz Activator.CreateInstance(assemblyName, typeName)poprosić rozdzielczość typu .NET, aby przekształciła ją w typ. Możesz to zawinąć za pomocą try / catch, aby w razie niepowodzenia przeszukać katalogi, w których możesz przechowywać dodatkowe zestawy, które w innym przypadku nie byłyby przeszukiwane. W tym momencie wykorzystano by poprzednią metodę.

Jeff Yates
źródło
2
Nie mam bezwzględnej ścieżki dll, więc assemlby.LoadFile ect. nie zadziała, jakieś inne pomysły?
MegaByte
@rp Zawsze chętny do pomocy (i spóźnia się z tym tylko rok!)
Jeff Yates
2
@MegaByte: LoadFrom różni się od LoadFile. To rozwiąże twoje zależności i powinno rozwiązać nazwę DLL ze znanych ścieżek (GAC, katalog exe itp.) Zobacz MSDN, aby uzyskać więcej informacji.
Jeff Yates
1
Jeszcze jedno ... (znowu ja) Um, nie możesz po prostu wpisać „MyType” jako nazwy typu, po nim musi być NAMESPACE. Byłoby to dokładniejsze:Type type = assembly.GetType("MyNamespace"+"."+"MyType");
Cipi
1
@Cipi: Technicznie typem jest pełna nazwa przestrzeni nazw (koncepcja przestrzeni nazw jest wygodą językową). Możesz mieć typ bez przestrzeni nazw w CLR - podałem tylko zbyt uproszczony przykład.
Jeff Yates
36

Rozważ ograniczenia różnych Load*metod. Z dokumentów MSDN ...

LoadFile nie ładuje plików do kontekstu LoadFrom i nie rozwiązuje zależności przy użyciu ścieżki ładowania, jak robi to metoda LoadFrom.

Więcej informacji na temat ładowania kontekstów można znaleźć w LoadFromdokumentach.

Anthony Mastrean
źródło
19

Activator.CreateInstance powinien działać.

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

Uwaga: Nazwa typu musi być w pełni kwalifikowanym typem.

Przykład:

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

foreach (object obj in aray)
{
    Console.WriteLine(obj);
}
tvanfosson
źródło
1
Tylko uwaga na ten temat: TypeNamemusi być w pełni wykwalifikowany. Musiałem nazwać to tak: Activator.CreateInstance("MyAssembly","MyAssembly.TypeName") I to zwraca ObjectHandle. Aby przejść do interfejsu, musisz zrobićObjectHandle.UnWrap()
Anthony Sottile,
7

Znalazłem to pytanie i niektóre odpowiedzi bardzo przydatne, ale miałem problemy ze ścieżką, więc ta odpowiedź obejmowałaby ładowanie biblioteki przez znalezienie ścieżki katalogu bin.

Pierwsze rozwiązanie:

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

Drugie rozwiązanie

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

Możesz używać tej samej zasady dla interfejsów (tworzysz klasę, ale rzutujesz na interfejs), takich jak:

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

Ten przykład dotyczy aplikacji sieci web, ale podobny można zastosować w przypadku aplikacji komputerowej, na przykład tylko ścieżkę można rozwiązać w inny sposób

Path.GetDirectoryName(Application.ExecutablePath)
Sofija
źródło
5

To jest łatwe.

Przykład z 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);
}

Oto link referencyjny

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

użytkownik3722131
źródło
To okropny sposób na wsparcie dynamicznego ładowania kodu. MS zawsze lubił zmuszać nas do podania zbyt wielu szczegółów.
Jaśniejsze
3

Począwszy od Framework v4.5 można użyć Activator.CreateInstanceFrom () w celu łatwego tworzenia instancji klas w złożeniach. Poniższy przykład pokazuje, jak z niego korzystać i jak wywołać metodę przekazującą parametry i otrzymującą wartość zwracaną.

    // 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
źródło
2
((ISomeInterface)Activator.CreateInstance(Assembly.LoadFile("somePath").GetTypes()[0])).SomeInterfaceMethod();
abatishchev
źródło
2

Możesz załadować zestaw za pomocą metod * Assembly.Load **. Za pomocą Activator.CreateInstance możesz tworzyć nowe wystąpienia, które chcesz. Pamiętaj, że musisz użyć pełnej nazwy typu klasy, którą chcesz załadować (na przykład Namespace.SubNamespace.ClassName ). Z wykorzystaniem metody InvokeMember tego typu klasy można wywołać metody na typ.

Weź również pod uwagę, że po załadowaniu zestawu nie można rozładować, dopóki cała AppDomain również nie zostanie rozładowana (jest to w zasadzie wyciek pamięci).

Dario Solera
źródło
2

W zależności od tego, jak istotna jest tego rodzaju funkcjonalność dla twojego projektu, możesz rozważyć coś takiego jak MEF, który zajmie się ładowaniem i wiązaniem ze sobą komponentów.

Kent Boogaart
źródło
2
Assembly assembly = Assembly.LoadFrom("MyAssembly.dll");

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

dynamic instanceOfMyType = Activator.CreateInstance(type);

W ten sposób możesz korzystać z funkcji, które nie są potrzebne do uzyskania metody methodinfo, a następnie jej wywołania. Zrobisz to tak jak instanceOfMyType.MethodName (); Ale nie można używać Intellisense, ponieważ typy dynamiczne są wpisywane w czasie wykonywania, a nie w czasie kompilacji.

David Mkheyan
źródło
1

Tak, to znaczy, będziesz chciał użyć statycznej metody Load na klasie Assembly, a następnie wywołać, a następnie wywołać metodę CreateInstance w instancji Assembly zwróconej z wywołania Load.

Możesz także wywołać jedną z pozostałych metod statycznych, zaczynając od „Load” na klasie Assembly, w zależności od potrzeb.

casperOne
źródło
0

Możesz to zrobić w następujący sposób:

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
źródło