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:

  1. Activator.CreateInstance(Type t) retorna um objeto, mas não consegui converter o objeto em uma interface que o objeto implementou.Por que?
  2. Eu deveria estar usando uma sobrecarga diferente de CreateInstance()?
  3. Quais são as dicas e truques relacionados à reflexão?
  4. Existe alguma parte crucial da reflexão que simplesmente não estou entendendo?
Foi útil?

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

@lubos hasko

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 é.

@Hackeado

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

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top