Pergunta sobre generics, c#, inheritance – Chamando um método genérico com o tipo derivado correto

9

Eu tenho o seguinte cenário:

Eu tenho três classes, vamos chamá-lasA, B eC. Tudo o que eles têm em comum é que eles herdam da mesma interface,ISomeInterface e que são classes mapeadas para entidades usando o Entity Framework.

Eu tenho um método que recebeu uma lista de objetos que implementa essa interface, mas os objetos em si serão instâncias deA, B ouC.

O shell do método se parece com isso

public void MyMethod(List<ISomeInterface> entityList)
{
  foreach(var entity in entityList)
  {
    ProcessEntity(entity);
  }
}

Agora, o problema é com oProcessEntity método. Este é um método genérico, que precisa recuperar a tabela de elementos correspondentes do banco de dados de acordo com o tipo ou entidade, de modo que fique assim:

public void ProcessEntity<T>(T entity)
{
  using( var repository = new DbRepository())
  {
    var set = repository.Set<T>();
    ...
  }
}

O problema é que a linhavar set = repository.Set<T>(); falha porqueT éISomeInterface neste caso, e não o tipo real (A, B ouC), por isso dá uma exceção que não pode se relacionar com o tipo dado, o que é compreensível.

Então, minha pergunta é: Como posso chamar ProcessEntity com o tipo real do objeto dentro da lista, e não o interfacetype que eles implementam.

Sua resposta

3   a resposta
2

você pode fazer um truque parecido com um visitante e usar a seguinte solução alternativa:

Definir um métodoProcess(EntityProcessor ep) emISomeInterfaceImplemente-o emA assim comoep.ProcessEntity<A>(this) (e da mesma maneiraB eC)Ao invés deProcessEntity(entity) no seu loop, basta ligarentity.Process(this).

(os nomes dos métodos talvez não sejam mais limpos, mas você deve ter a ideia)

@ Øyvind: no meu exemplo, uma nova função é adicionada aoISomeInterface (que é de fato um tipo de suporte ao padrão de visitantes). Desta forma, todosentity tem que implementá-lo, como ele implementaISomeInterface. Vlad
Sim, e é isso que estou evitando usando dinâmica. Então, a desvantagem nesse caso é perder algum tipo de segurança (e eu estou bem com isso neste caso, já que é apenas dentro desse método) para manter as classes mais limpas não forçando algum método de interface nelas que deve ser implementado por todos eles. Øyvind Bråthen
Totalmente acordado lá. É um trade-off como tudo mais. Não tenho certeza do que você está se referindo pelo membro da interface tho. A implementação que acabei não usa nenhum membro da interface, apenas verifica se a classe implementa a interface, mas a implementação real da interface é usada para outra coisa, e não para essa funcionalidade específica. Øyvind Bråthen
Eu vejo o seu ponto, mas usando dinâmica você não tem um método necessário, e eu preferiria que as classes A, B e C não tivessem nenhum conhecimento sobre essa funcionalidade também. Isso também significa que eu não preciso lembrar de colocar essa funcionalidade extra para novas classes criadas que devem seguir o mesmo padrão. Øyvind Bråthen
14

Você pode aplicardynamic palavra-chave ao passar a entidade para ProcessEntity. Nesse caso, o tipo real de entidade será determinado em tempo de execução.

public void MyMethod(List<ISomeInterface> entityList)
{
  foreach(var entity in entityList)
  {
    dynamic obj = entity;
    ProcessEntity(obj);
  }
}
Isso fez o truque. eu mudoProcessEntity(obj); paraProcessEntity(obj as dynamic);e isso funcionou muito bem. Um uso para dinâmica que eu não conhecia. Muito obrigado :) Øyvind Bråthen
@ ØyvindKnobloch-Bråthen sim, eu gosto muito dessa tipificação de tempo de execução Sergey Berezovskiy
2

e, em seguida, chamá-lo, por exemplo:

var method = typeof(ClassContainingProcessEntity)
    .GetMethod(ProcessEntity)
    .MakeGenericMethod(entity.GetType);
method.Invoke(this, entity);

Você poderia armazenar em cache o método por tipo, e você poderia compilá-lo em tempo de execução usando algum tipo de fábrica delegada se o desempenho for crítico.

Alternativamente, você poderia usar oPadrão visitante

Sim, ambos devem funcionar, e eu estava pensando em seguir o caminho da reflexão se não obtivesse uma resposta melhor aqui. Parece que as correções dinâmicas são muito mais limpas. Obrigado pela sua contribuição :) Øyvind Bråthen

Perguntas relacionadas