Método de extensão e objeto dinâmico

96

Vou resumir meu problema no seguinte trecho de código.

List<int> list = new List<int>() { 5, 56, 2, 4, 63, 2 };
Console.WriteLine(list.First());

O código acima está funcionando bem.

Agora eu tentei o seguinte

dynamic dList = list;
 Console.WriteLine(dList.First());

mas estou recebendo RuntimeBinderException. Por que isso acontece?

santosh singh
fonte
Esta parece ser uma duplicata desta pergunta feita apenas 4 dias atrás stackoverflow.com/questions/5270782/…
jbtule
@jbtule A diferença é que o thisé dinâmico aqui, mas se você pousar aqui, provavelmente também deve olhar para essa questão
nik.shornikov

Respostas:

131

Para expandir a resposta de Stecya ... os métodos de extensão não são suportados por tipagem dinâmica na forma de métodos de extensão , ou seja, chamados como se fossem métodos de instância. No entanto, isso funcionará:

dynamic dList = list;
Console.WriteLine(Enumerable.First(dList));

Claro, isso pode ou não ser útil. Se você pudesse fornecer mais informações sobre por que e como está tentando usar a digitação dinâmica, talvez possamos ajudar mais.

Jon Skeet
fonte
Eu estava brincando com um objeto dinâmico e recebi esta exceção. Você escreveu algum artigo sobre este tópico, onde usar ou não usar objeto dinâmico
santosh singh
19
@geek: Pessoalmente, minha regra é usar apenas dynamiconde você realmente precisa ... basicamente, se de outra forma você acessasse os membros com reflexão, isso é um grande sinal. Por outro lado, sou um digitador estático obstinado - outros podem sugerir políticas menos pessimistas :)
Jon Skeet
2
Pode ser mais legível lançar de volta para o tipo conhecido, isso funciona: Console.WriteLine (((List <int>) dList) .First ()); Ou Console.WriteLine ((dList as List <int>) .First ()) ;.
AVee
138

Para expandir a resposta de Jon, o motivo pelo qual isso não funciona é porque, em métodos regulares de extensão de código não dinâmico, funcionam fazendo uma pesquisa completa em todas as classes conhecidas pelo compilador para uma classe estática que possui um método de extensão correspondente. A pesquisa segue em ordem com base no aninhamento do namespace e usingnas diretivas disponíveis em cada namespace.

Isso significa que, para obter uma invocação de método de extensão dinâmica resolvida corretamente, de alguma forma o DLR precisa saber em tempo de execução quais são todos os aninhamentos de namespace e usingdiretivas em seu código-fonte . Não temos um mecanismo útil para codificar todas essas informações no site da chamada. Pensamos em inventar tal mecanismo, mas decidimos que era um custo muito alto e gerava muito risco de cronograma para valer a pena.

Eric Lippert
fonte
Muito obrigado pela explicação.
santosh singh
3
Esse recurso é iminente? Certamente seria uma mudança significativa; chamadas que atualmente lançam RunTimeBinderExceptions começariam repentinamente a funcionar na recompilação da origem. Além disso, haveria algum risco de segurança associado à implementação de tal recurso?
Ani
5
@ani: Estamos planejando implementar esse recurso? Não. Existem riscos de segurança? Eu não estou ciente de nenhum; que tipo de risco de segurança você tinha em mente? Comece dizendo quem é o invasor e que ameaça ele está fazendo ao usuário.
Eric Lippert
@EricLippert, entendi que todos os dynamicobjetos são iguais a C # :, DynamicObjectentão não há como diferenciá-los e esse é um dos motivos pelos quais não é possível adicionar métodos de extensão dynamic, certo?
Tom Sarduy
@EricLippert considera expandir esta resposta um pouco mais e adicionar frases ao longo das linhas de "Quando qualquer um dos parâmetros é dinâmico, todas as resoluções são adiadas até o tempo de execução". Embora seja óbvio para você que essa parte importante é difícil de encontrar em qualquer outro lugar no SO (consulte stackoverflow.com/questions/48324768 por exemplo)
Alexei Levenkov
18

Porque First()não é um método de List. É definido na extensão Linq paraIEnumerable<>

Stecya
fonte