Delegados no .NET: Como eles são construídos?
-
19-09-2019 - |
Pergunta
Ao inspecionar os delegados em C# e .NET em geral, notei alguns fatos interessantes:
Criar um delegado em C# cria uma classe derivada de MulticastDelegate
com um construtor:
.method public hidebysig specialname rtspecialname instance void .ctor(object 'object', native int 'method') runtime managed { }
O que significa que espera a instância e um ponteiro para o método. No entanto, a sintaxe da construção de um delegado em C# sugere que ele tem um construtor
new MyDelegate(int () target)
onde posso reconhecer int ()
Como uma instância de função (int *target()
seria um ponteiro de função em C ++). Então, obviamente, o compilador C# escolhe o método correto do grupo de métodos definido pelo nome da função e constrói o delegado. Portanto, a primeira pergunta seria: de onde o compilador C# (ou o Visual Studio, para ser preciso) escolhe essa assinatura do construtor? Não notei nenhum atributo especial ou algo que faria uma distinção. Isso é algum tipo de compilador/visualstudio Magic? Se não, é o T (args) target
Construção válida em C#? Não consegui conseguir nada para compilar, por exemplo:
int () alvo = myMethod;
é inválido, assim está fazendo qualquer coisa com MyMetod
, por exemplo, chamando .ToString()
nele (bem, isso faz algum sentido, pois esse é tecnicamente um método grupo, mas imagino que deveria ser possível escolher explicitamente um método lançando, por exemplo (int())MyFunction
. Então, tudo isso é mágica puramente compiladora? Olhar para a construção através do refletor revela mais uma sintaxe:
Func cs $ 1 $ 0000 = novo func (null, (intptr) foo);
Isso é consistente com a assinatura do construtor desmontado, mas isso não compila!
Uma nota interessante final é que as aulas Delegate
e MulticastDelegate
tem mais alguns conjuntos de construtores:
.Method Family hideBysig SpecialName RTSpecialName Instância void .Ctor (classe System.Type Target, String 'Method') CIL gerenciado
Onde ocorre a transição de um ponteiro de instância e método para um tipo e um nome de método de string? Isso pode ser explicado pelo runtime managed
Palavras -chave na assinatura do construtor de delegados personalizados, ou seja, o tempo de execução faz o trabalho aqui?
EDITAR: OK, então acho que devo reformular o que queria dizer com esta pergunta. Basicamente, estou sugerindo que não há apenas C# Compiler / CLR Magic envolvido na construção de delegados, mas também alguns Estúdio visual A magia, uma vez que o Intellisense vira uma nova sintaxe ao sugerir os argumentos do construtor e até esconde um deles (por exemplo, o refletor não usa essa sintaxe e construtor, nesse caso).
Eu queria saber se essa afirmação é verdadeira e se a sintaxe da instância da função tem algum significado mais profundo em C# ou é apenas algum formato constante implementado pela parte Magic Visual Studio para clareza (o que faz sentido, pois parece inválido C#)? Em suma, se eu estivesse implementando o Intellisense, devo fazer alguma mágica para delegados ou poderia construir a sugestão por algum mecanismo inteligente?
Edição final: Então, o consenso popular é que isso é realmente vs magia. Ver outros exemplos (veja o comentário de Marc Granell) de tais vs Comportamento me convence de que esse é o caso.
Solução
O primeiro argumento é resolvido a partir da referência do objeto (ou null
para métodos estáticos); Sem mágica lá.
Re the segundo argumento, no entanto - é um ponteiro não gerenciado (nativo int); em suma, não há alternativa direto C# sintaxe que pode usar este construtor - ele usa uma instrução IL específica (ldftn
) para resolver a função dos metadados. No entanto, você pode usar Delegate.CreateDelegate
para criar delegados via reflexão. Você também pode usar o IL emit (DynamicMethod
etc), mas não é divertido.
Outras dicas
Você primeiro define o delegado (é assim que o Visual Studio conhece a assinatura do método de destino):
delegate void MyDelegate();
Então você constrói instâncias delegadas como esta:
MyDelegate method = new MyDelegate({method name});
// If this was the method you wanted to invoke:
void MethodToInvoke()
{
// do something
}
MyDelegate method = new MyDelegate(MethodToInvoke);
C# escolhe automaticamente o método que corresponde à assinatura do delegado.
Editar: Quando o Intellisense do Visual Studio mostra o int () target
Sugestão, está mostrando a assinatura dos métodos C# que você pode usar. O compilador C# traduz a representação C# para IL. A implementação da IL parecerá diferente, porque o IL não é uma linguagem de estilo C e o compilador C# fornece açúcar sintático para abstrair os detalhes da implementação.
Isso é apenas um palpite, então não me atire se eu estiver errado, mas acho que o Intellisense está recebendo a assinatura do método de destino do Invoke
Método definido no delegado. Refletor mostra claramente que o Invoke
método em System.Action<T>
é:
[MethodImpl(0, MethodCodeType=MethodCodeType.Runtime)]
public virtual void Invoke(T obj);
Que é o mesmo que a sugestão de assinatura oferecida pelo IntelliSense. A mágica é Intellisense, ao ver um tipo de delegado, olha para o Invoke
Método e sugere um construtor que leva um alvo que o corresponde.