Estou tentando descobrir como você poderia importar e usar um .dll em tempo de execução dentro de um aplicativo C #. Usando Assembly.LoadFile () consegui fazer com que meu programa carregasse a dll (esta parte está definitivamente funcionando porque consigo obter o nome da classe com ToString ()), mas não consigo usar o 'Output' método de dentro do meu aplicativo de console. Estou compilando o .dll e movendo-o para o projeto do meu console. Existe uma etapa extra entre CreateInstance e, em seguida, ser capaz de usar os métodos?
Esta é a classe em minha DLL:
namespace DLL
{
using System;
public class Class1
{
public void Output(string s)
{
Console.WriteLine(s);
}
}
}
e aqui está o aplicativo que desejo carregar a DLL
namespace ConsoleApplication1
{
using System;
using System.Reflection;
class Program
{
static void Main(string[] args)
{
var DLL = Assembly.LoadFile(@"C:\visual studio 2012\Projects\ConsoleApplication1\ConsoleApplication1\DLL.dll");
foreach(Type type in DLL.GetExportedTypes())
{
var c = Activator.CreateInstance(type);
c.Output(@"Hello");
}
Console.ReadLine();
}
}
}
c#
reflection
dll
danbroooks
fonte
fonte
Respostas:
Os membros devem ser resolvidos em tempo de compilação para serem chamados diretamente do C #. Caso contrário, você deve usar reflexão ou objetos dinâmicos.
Reflexão
namespace ConsoleApplication1 { using System; using System.Reflection; class Program { static void Main(string[] args) { var DLL = Assembly.LoadFile(@"C:\visual studio 2012\Projects\ConsoleApplication1\ConsoleApplication1\DLL.dll"); foreach(Type type in DLL.GetExportedTypes()) { var c = Activator.CreateInstance(type); type.InvokeMember("Output", BindingFlags.InvokeMethod, null, c, new object[] {@"Hello"}); } Console.ReadLine(); } } }
Dinâmico (.NET 4.0)
namespace ConsoleApplication1 { using System; using System.Reflection; class Program { static void Main(string[] args) { var DLL = Assembly.LoadFile(@"C:\visual studio 2012\Projects\ConsoleApplication1\ConsoleApplication1\DLL.dll"); foreach(Type type in DLL.GetExportedTypes()) { dynamic c = Activator.CreateInstance(type); c.Output(@"Hello"); } Console.ReadLine(); } } }
fonte
Output
todos os tipos na montagem, o que provavelmente ocorrerá antes que a classe "certa" seja encontrada ...IDog dog = someInstance as IDog;
e testar se não for nulo. Coloque suas interfaces em uma DLL comum compartilhada pelos clientes, e qualquer plugin que será carregado dinamicamente deve implementar essa interface. Isso permitirá que você codifique seu cliente na interface do IDog e tenha a verificação de tipo intellisense + forte em tempo de compilação ao invés de usar dinâmico.Class1
. Nesse ponto, você pode apenas usarnew Class1()
. O solicitante especificou explicitamente uma dependência de tempo de execução.dynamic
permite que o programa não exija nenhuma dependência de tempo de compilaçãoClass1
.Agora, você está criando uma instância de cada tipo definido na montagem . Você só precisa criar uma única instância de
Class1
para chamar o método:class Program { static void Main(string[] args) { var DLL = Assembly.LoadFile(@"C:\visual studio 2012\Projects\ConsoleApplication1\ConsoleApplication1\DLL.dll"); var theType = DLL.GetType("DLL.Class1"); var c = Activator.CreateInstance(theType); var method = theType.GetMethod("Output"); method.Invoke(c, new object[]{@"Hello"}); Console.ReadLine(); } }
fonte
Você precisa criar uma instância do tipo que expõe o
Output
método:static void Main(string[] args) { var DLL = Assembly.LoadFile(@"C:\visual studio 2012\Projects\ConsoleApplication1\ConsoleApplication1\DLL.dll"); var class1Type = DLL.GetType("DLL.Class1"); //Now you can use reflection or dynamic to call the method. I will show you the dynamic way dynamic c = Activator.CreateInstance(class1Type); c.Output(@"Hello"); Console.ReadLine(); }
fonte
Activator.CreateInstance()
retorna um objeto, que não tem um método de saída.Parece que você vem de linguagens de programação dinâmicas? Definitivamente, C # não é isso, e o que você está tentando fazer será difícil.
Já que você está carregando uma dll específica de um local específico, talvez queira apenas adicioná-la como uma referência ao seu aplicativo de console?
Se você realmente deseja carregar a montagem via
Assembly.Load
, terá que ir via reflexão para chamar qualquer membroc
Algo como
type.GetMethod("Output").Invoke(c, null);
deveria servir.fonte
foreach (var f in Directory.GetFiles(".", "*.dll")) Assembly.LoadFrom(f);
Isso carrega todas as DLLs presentes na pasta do seu executável.
No meu caso estava tentando usar
Reflection
para encontrar todas as subclasses de uma classe, até mesmo em outras DLLs. Isso funcionou, mas não tenho certeza se é a melhor maneira de fazer isso.EDIT: Eu cronometrei, e só parece carregá-los na primeira vez.
Stopwatch stopwatch = new Stopwatch(); for (int i = 0; i < 4; i++) { stopwatch.Restart(); foreach (var f in Directory.GetFiles(".", "*.dll")) Assembly.LoadFrom(f); stopwatch.Stop(); Console.WriteLine(stopwatch.ElapsedMilliseconds); }
Saída: 34 0 0 0
Portanto, é possível executar esse código antes de qualquer pesquisa do Reflection, apenas para garantir.
fonte
Não é tão difícil.
Você pode inspecionar as funções disponíveis do objeto carregado e, se encontrar o que está procurando pelo nome, espiar seus parâmetros esperados, se houver. Se for a chamada que você está tentando encontrar, chame-a usando o método Invoke do objeto MethodInfo.
Outra opção é simplesmente construir seus objetos externos para uma interface e lançar o objeto carregado para essa interface. Se for bem-sucedido, chame a função nativamente.
Isso é uma coisa muito simples.
fonte