Posso carregar um assembly .NET em tempo de execução e instanciar um tipo que sabe apenas o nome?

178

É possível instanciar um objeto em tempo de execução se eu tiver apenas o nome da DLL e o nome da classe, sem adicionar uma referência ao assembly no projeto? A classe implementa uma interface, portanto, quando eu instanciar a classe, a converteremos na interface.

Nome da montagem:

library.dll

Digite o nome:

Company.Project.Classname


Edição: Eu não tenho o caminho absoluto da DLL, por Assembly.LoadFileisso não vai funcionar. A DLL pode estar na raiz do aplicativo, system32 ou mesmo carregada no GAC.

Megabyte
fonte

Respostas:

221

Sim. Você precisa usar Assembly.LoadFrompara carregar a montagem na memória e, em seguida, Activator.CreateInstancecriar uma instância do seu tipo preferido. Você precisará procurar o tipo primeiro usando reflexão. Aqui está um exemplo simples:

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

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

object instanceOfMyType = Activator.CreateInstance(type);

Atualizar

Quando você tem o nome do arquivo de montagem e o nome do tipo, pode Activator.CreateInstance(assemblyName, typeName)solicitar à resolução de tipo .NET que resolva isso em um tipo. Você pode agrupar isso com uma tentativa / captura, para que, se falhar, você pode executar uma pesquisa de diretórios nos quais pode armazenar especificamente assemblies adicionais que, de outra forma, não seriam pesquisados. Isso usaria o método anterior nesse ponto.

Jeff Yates
fonte
2
Eu não tenho o caminho absoluto da dll, então assemlby.LoadFile ect. não vai funcionar, outras idéias?
MegaByte
@rp sempre dispostos a ajudar (e só um ano de atraso em dizer isso!)
Jeff Yates
2
@MegaByte: LoadFrom é diferente de LoadFile. Ele resolverá suas dependências e o nome da DLL de caminhos conhecidos (GAC, diretório exe etc.). Consulte o MSDN para obter mais informações.
Jeff Yates
1
Só mais uma coisa ... (eu novamente) Hum, você não pode simplesmente ter "MyType" como o nome do tipo, ele deve ser seguido por NAMESPACE. Portanto, isso seria mais preciso:Type type = assembly.GetType("MyNamespace"+"."+"MyType");
Cipi 14/02
1
@ Cipi: Tecnicamente, um tipo é o nome completo no namespace (o conceito de namespace é uma conveniência de idioma). Você pode ter um tipo sem espaço para nome no CLR - eu estava apenas fornecendo um exemplo excessivamente simplificado.
Jeff Yates
36

Considere as limitações dos diferentes Load*métodos. Dos documentos do MSDN ...

O LoadFile não carrega arquivos no contexto LoadFrom e não resolve dependências usando o caminho de carregamento, como o método LoadFrom.

Mais informações sobre Contextos de carregamento podem ser encontradas nos LoadFromdocumentos.

Anthony Mastrean
fonte
19

Activator.CreateInstance deve funcionar.

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

Nota: O nome do tipo deve ser o tipo completo.

Exemplo:

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

foreach (object obj in aray)
{
    Console.WriteLine(obj);
}
tvanfosson
fonte
1
Apenas uma observação: TypeNamedeve ser totalmente qualificado. Eu tive que chamar isso assim: Activator.CreateInstance("MyAssembly","MyAssembly.TypeName") E isso retorna um ObjectHandle. Para começar a sua interface que você precisa fazerObjectHandle.UnWrap()
Anthony Sottile
7

Encontrei essa pergunta e algumas respostas muito úteis, porém tive problemas de caminho, portanto, essa resposta abrangeria o carregamento da biblioteca encontrando o caminho do diretório bin.

Primeira solução:

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

Segunda solução

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

Você pode usar o mesmo princípio para interfaces (você criaria uma classe, mas converterá em interface), como:

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

Este exemplo é para aplicativo Web, mas similar pode ser usado para aplicativo Desktop, apenas o caminho é resolvido de maneira diferente, por exemplo

Path.GetDirectoryName(Application.ExecutablePath)
Sofija
fonte
5

É fácil.

Exemplo do 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);
}

Aqui está um link de referência

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

user3722131
fonte
Essa é uma maneira horrível de oferecer suporte ao carregamento dinâmico de código. A MS sempre gostou de nos forçar a entrar em muitos detalhes.
Clearer
3

A partir do Framework v4.5, você pode usar o Activator.CreateInstanceFrom () para instanciar facilmente as classes nos assemblies. O exemplo a seguir mostra como usá-lo e como chamar um método passando parâmetros e obtendo valor de retorno.

    // 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
fonte
2
((ISomeInterface)Activator.CreateInstance(Assembly.LoadFile("somePath").GetTypes()[0])).SomeInterfaceMethod();
abatishchev
fonte
2

Você pode carregar uma montagem usando os métodos * Assembly.Load **. Usando Activator.CreateInstance, você pode criar novas instâncias do tipo desejado. Lembre-se de que você deve usar o nome completo do tipo da classe que deseja carregar (por exemplo, Namespace.SubNamespace.ClassName ). Usando o método InvokeMember do Type classe você pode invocar métodos no tipo.

Além disso, leve em consideração que, uma vez carregado, um assembly não pode ser descarregado até que todo o AppDomain também seja descarregado (isso é basicamente um vazamento de memória).

Dario Solera
fonte
2

Dependendo do quão intrínseco esse tipo de funcionalidade é para o seu projeto, você pode considerar algo como o MEF, que cuidará do carregamento e da união de componentes para você.

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

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

dynamic instanceOfMyType = Activator.CreateInstance(type);

Portanto, dessa maneira, você pode usar funções que não sejam a obtenção de methodinfo e, em seguida, invocá-lo. Você fará isso como instanceOfMyType.MethodName (); Mas você não pode usar o Intellisense porque os tipos dinâmicos são digitados em tempo de execução, não em tempo de compilação.

David Mkheyan
fonte
1

Sim, você desejará usar o método Load estático na classe Assembly e, em seguida, chame e, em seguida, chame o método CreateInstance na instância Assembly retornada da chamada para Load.

Além disso, você pode chamar um dos outros métodos estáticos começando com "Load" na classe Assembly, dependendo de suas necessidades.

casperOne
fonte
0

Você pode fazer isso desta maneira:

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
fonte