Могу ли я загрузить сборку .NET во время выполнения и создать экземпляр типа, зная только имя?

178

Можно ли создать экземпляр объекта во время выполнения, если у меня есть только имя DLL и имя класса, без добавления ссылки на сборку в проекте? Класс реализует интерфейс, поэтому, как только я создам экземпляр класса, я приведу его к интерфейсу.

Название сборки:

Library.dll

Введите имя:

Company.Project.Classname


РЕДАКТИРОВАТЬ: У меня нет абсолютного пути к DLL, поэтому Assembly.LoadFileне будет работать. DLL может находиться в корне приложения, system32 или даже загружаться в GAC.

MegaByte
источник

Ответы:

221

Да. Вам нужно использовать Assembly.LoadFromдля загрузки сборки в память, затем вы можете использовать Activator.CreateInstanceдля создания экземпляра вашего предпочтительного типа. Сначала вам нужно будет найти тип с помощью отражения. Вот простой пример:

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

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

object instanceOfMyType = Activator.CreateInstance(type);

Обновить

Если у вас есть имя файла сборки и имя типа, вы можете использовать Activator.CreateInstance(assemblyName, typeName)запрос типа разрешения .NET, чтобы преобразовать его в тип. Вы можете обернуть это с помощью try / catch, чтобы в случае неудачи вы могли выполнить поиск по каталогам, где вы можете специально хранить дополнительные сборки, которые иначе не могли бы быть найдены. Это будет использовать предыдущий метод в этой точке.

Джефф Йейтс
источник
2
У меня нет абсолютного пути к DLL, так что сборка.LoadFile и т. Д. не будет работать, какие-нибудь другие идеи?
MegaByte
@rp Всегда рад помочь (и только год опаздывает с этим!)
Джефф Йейтс
2
@MegaByte: LoadFrom отличается от LoadFile. Он разрешит ваши зависимости и разрешит имя DLL из известных путей (GAC, exe-каталог и т. Д.). Дополнительные сведения см. В MSDN.
Джефф Йейтс
1
Еще одна вещь ... (я снова) Хм, вы не можете просто иметь "MyType" в качестве имени типа, за ним должно следовать NAMESPACE. Так что это было бы более точно:Type type = assembly.GetType("MyNamespace"+"."+"MyType");
Cipi
1
@Cipi: Технически тип - это полное имя в пространстве имен (концепция пространства имен - это удобство языка). У вас может быть тип без пространства имен в CLR - я просто приводил слишком упрощенный пример.
Джефф Йейтс
36

Рассмотрим ограничения различных Load*методов. Из документов MSDN ...

LoadFile не загружает файлы в контекст LoadFrom и не разрешает зависимости, используя путь загрузки, как это делает метод LoadFrom.

Более подробную информацию о контекстах загрузки можно найти в LoadFromдокументации.

Энтони Мастреан
источник
19

Activator.CreateInstance должен работать.

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

Примечание: имя типа должно быть полностью определенным типом.

Пример:

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

foreach (object obj in aray)
{
    Console.WriteLine(obj);
}
tvanfosson
источник
1
Просто примечание по этому TypeNameповоду : должно быть полностью квалифицированным. Я должен был назвать это как: Activator.CreateInstance("MyAssembly","MyAssembly.TypeName") И это возвращает ObjectHandle. Чтобы перейти к вашему интерфейсу, вам нужно сделатьObjectHandle.UnWrap()
Энтони Соттил
7

Я нашел этот вопрос и некоторые ответы очень полезными, однако у меня были проблемы с путями, поэтому этот ответ охватывал бы загрузку библиотеки путем нахождения пути к каталогу bin.

Первое решение:

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

Второе решение

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

Вы можете использовать тот же принцип для интерфейсов (вы будете создавать класс, но приведение к интерфейсу), например:

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

Этот пример относится к веб-приложению, но подобное можно использовать и для настольных приложений, иначе разрешается только путь, например

Path.GetDirectoryName(Application.ExecutablePath)
Sofija
источник
5

Это просто.

Пример из 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);
}

Вот ссылка ссылка

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

user3722131
источник
Это ужасный способ поддерживать динамическую загрузку кода. MS всегда любил заставлять нас вдаваться в подробности.
уточнение
3

Начиная с Framework v4.5, вы можете использовать Activator.CreateInstanceFrom (), чтобы легко создавать экземпляры классов в сборках. В следующем примере показано, как его использовать и как вызывать метод, передавая параметры и получая возвращаемое значение.

    // 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
источник
2

Да. У меня нет никаких примеров, которые я сделал лично, прямо сейчас. Я выложу позже, когда найду. В основном вы будете использовать отражение, чтобы загрузить сборку, а затем вытащить любые типы, которые вам нужны.

А пока эта ссылка должна помочь вам начать:

Использование отражения для загрузки сборок без ссылок во время выполнения

Джованни Гальбо
источник
2
((ISomeInterface)Activator.CreateInstance(Assembly.LoadFile("somePath").GetTypes()[0])).SomeInterfaceMethod();
abatishchev
источник
2

Вы можете загрузить сборку, используя методы * Assembly.Load **. Используя Activator.CreateInstance вы можете создавать новые экземпляры нужного вам типа. Помните, что вы должны использовать полное имя типа класса, который вы хотите загрузить (например, Namespace.SubNamespace.ClassName ). Использование метода InvokeMember от типа класса вы можете вызывать методы типа.

Кроме того, примите во внимание, что после загрузки сборка не может быть выгружена до тех пор, пока не будет выгружен весь домен приложений (в основном это утечка памяти).

Дарио Солера
источник
2

В зависимости от того, насколько эта функциональность присуща вашему проекту, вы можете рассмотреть что-то вроде MEF, которое позаботится о загрузке и связывании компонентов для вас.

Кент Бугаарт
источник
2
Assembly assembly = Assembly.LoadFrom("MyAssembly.dll");

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

dynamic instanceOfMyType = Activator.CreateInstance(type);

Таким образом, таким образом вы можете использовать функции, не получая methodinfo, а затем вызывая его. Вам будет делать это instanceOfMyType.MethodName (); Но вы не можете использовать Intellisense, потому что динамические типы вводятся во время выполнения, а не во время компиляции.

Давид Мхеян
источник
1

Да, вам нужно использовать статический метод Load в классе Assembly, а затем вызвать, а затем вызвать метод CreateInstance для экземпляра Assembly, возвращенного вам из вызова Load.

Кроме того, вы можете вызывать один из других статических методов, начиная с «Load» в классе Assembly, в зависимости от ваших потребностей.

casperOne
источник
0

Вы можете сделать это следующим образом:

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
источник