Como lançar corretamente objetos criados por meio de reflexão
-
09-06-2019 - |
Pergunta
Estou tentando entender a reflexão, então decidi adicionar capacidade de plug-in a um programa que estou escrevendo.A única maneira de entender um conceito é sujar os dedos e escrever o código, então segui o caminho de criar uma biblioteca de interface simples que consiste nas interfaces IPlugin e IHost, uma biblioteca de implementação de plugins de classes que implementam IPlugin e um simples projeto de console que instancia a classe de implementação IHost que faz um trabalho simples com os objetos do plugin.
Usando reflexão, eu queria iterar pelos tipos contidos na dll de implementação do meu plugin e criar instâncias de tipos.Consegui instanciar classes com este código, mas não consegui lançar o objeto criado para a interface.
Tentei esse código, mas não consegui converter o objeto como esperava.Percorri o processo com o depurador e o construtor adequado foi chamado.O objeto Quickwatching me mostrou que ele tinha os campos e propriedades que eu esperava ver na classe de implementação.
loop through assemblies
loop through types in assembly
// Filter out unwanted types
if (!type.IsClass || type.IsNotPublic || type.IsAbstract )
continue;
// This successfully created the right object
object o = Activator.CreateInstance(type);
// This threw an Invalid Cast Exception or returned null for an "as" cast
// even though the object implemented IPlugin
IPlugin i = (IPlugin) o;
Eu fiz o código funcionar com isso.
using System.Runtime.Remoting;
ObjectHandle oh = Activator.CreateInstance(assembly.FullName, type.FullName);
// This worked as I intended
IPlugin i = (IPlugin) oh.Unwrap();
i.DoStuff();
Aqui estão minhas perguntas:
- Activator.CreateInstance(Type t) retorna um objeto, mas não consegui converter o objeto em uma interface que o objeto implementou.Por que?
- Eu deveria estar usando uma sobrecarga diferente de CreateInstance()?
- Quais são as dicas e truques relacionados à reflexão?
- Existe alguma parte crucial da reflexão que simplesmente não estou entendendo?
Solução
Só estou supondo aqui porque a partir do seu código não é óbvio onde você tem a definição da interface IPlugin, mas se você não pode transmitir em seu aplicativo host, provavelmente você está tendo a interface IPlugin em seu assembly host e ao mesmo tempo em sua montagem de plug-in.Isso não vai funcionar.
A coisa mais fácil de fazer isso funcionar é ter a interface IPlugin marcada como pública em seu assembly Host e então ter seu assembly Plugin montagem do aplicativo host de referência, então ambos os assemblies têm acesso a a mesma interface.
Outras dicas
hmmm...Se você estiver usando Assembly.LoadFrom para carregar sua montagem, tente alterá-la Assembly.LoadFile.
Funcionou para mim
Daqui: http://www.eggheadcafe.com/community/aspnet/2/10036776/solution-found.aspx
Você acertou em cheio.Meu design original tinha três assemblies diferentes, com a implementação do host e do plugin fazendo referência ao assembly da interface do plugin.
Tentei uma solução separada com uma implementação de host e montagem de interface e uma montagem de implementação de plugin.Dentro dessa solução, o código do primeiro bloco funcionou conforme o esperado.
Você me deu um pouco mais em que pensar, porque não estou entendendo muito bem por que dois assemblies que fazem referência a um assembly comum não obtêm o mesmo tipo do assembly comum.
Eu estava apenas tentando resolver isso sozinho e consegui tropeçar na resposta!
Eu tive 3 projetos C# diferentes
- A - Projeto de interface de plug-in
- B - Projeto Host exe -> referências A
- C - Projeto de implementação do plugin -> referências A
Eu também estava recebendo o erro de conversão até alterar o nome do assembly do meu projeto Plugin Interface para corresponder ao namespace para o qual eu estava tentando transmitir.
Por exemplo.
IPluginModule pluginModule = (IPluginModule)Activator.CreateInstance(curType);
estava falhando porque o assembly em que a interface IPluginModule foi definida era chamado de 'Common'. No entanto, o -type- para o qual eu estava lançando era 'Blah.Plugins.Common.IPluginModule'.
Mudei o nome do assembly do projeto de interface para 'Blah.Plugins.Common', o que significa que a conversão foi bem-sucedida.
Espero que esta explicação ajude alguém.De volta ao código..
Seu tipo não é público? Em caso afirmativo, chame a sobrecarga que recebe um booleano:
Activator.CreateInstance(type, true);
Além disso, no seu primeiro exemplo, veja se o é nulo e, se não, imprima o.GetType().Name para ver o que realmente é.
Tentei manter o pseudocódigo simples.foreach ocupa muito espaço e chaves.Eu esclareci isso.
o.GetType().FullName retorna Plugins.Multiply que é o objeto esperado.Plugins.Multiply implementa IPlugin.Passei pelo processo no depurador algumas vezes até desistir à noite.Não consegui entender por que não consegui lançá-lo, porque observei o construtor disparar até ficar irritado com toda a bagunça.Voltei esta noite e fiz funcionar, mas ainda não entendo por que a conversão falhou no primeiro bloco de código.O segundo bloco de código funciona, mas parece estranho para mim.
O link para egghead acima é a principal solução para o problema, use Assembly.LoadFile() em vez de .LoadFrom()