Delegati in .NET:come sono costruiti?
-
19-09-2019 - |
Domanda
Durante l'ispezione dei delegati in C# e .NET in generale, ho notato alcuni fatti interessanti:
La creazione di un delegato in C# crea una classe derivata da MulticastDelegate
con un costruttore:
.method public hidebysig specialname rtspecialname instance void .ctor(object 'object', native int 'method') runtime managed { }
Ciò significa che si aspetta l'istanza e un puntatore al metodo.Tuttavia, la sintassi della costruzione di un delegato in C# suggerisce che disponga di un costruttore
new MyDelegate(int () target)
dove posso riconoscere int ()
come istanza di funzione (int *target()
sarebbe un puntatore a funzione in C++).Quindi ovviamente il compilatore C# seleziona il metodo corretto dal gruppo di metodi definito dal nome della funzione e costruisce il delegato.Quindi la prima domanda sarebbe: da dove prende il compilatore C# (o Visual Studio, per la precisione) questa firma del costruttore?Non ho notato alcun attributo speciale o qualcosa che potesse fare una distinzione.È una sorta di magia del compilatore/visualstudio?In caso contrario, è il T (args) target
costruzione valida in C#?Non sono riuscito a ottenere nulla da compilare, ad esempio:
int() destinazione = MioMetodo;
non è valido, così come qualsiasi cosa con MyMetod
, per esempio.chiamando .ToString()
su di esso (beh, questo ha un senso, dato che tecnicamente è un metodo gruppo, ma immagino che dovrebbe essere possibile scegliere esplicitamente un metodo tramite casting, ad es. (int())MyFunction
.Quindi tutta questa è magia puramente del compilatore?Osservando la costruzione attraverso un riflettore si rivela ancora un'altra sintassi:
Funz CS$1$0000 = nuova Funz(null, (IntPtr) Foo);
Ciò è coerente con la firma del costruttore disassemblato, ma non viene compilato!
Un'ultima nota interessante è che le classi Delegate
E MulticastDelegate
hanno ancora un altro set di costruttori:
.Method Famiglia HideBysig SpecialName RtspecialName istanza void .Ctor (classe System.Type Target, String 'Method') Cil gestito
Dove avviene la transizione da un'istanza e un puntatore al metodo a un tipo e al nome di un metodo stringa?Ciò può essere spiegato dal runtime managed
parole chiave nella firma del costruttore del delegato personalizzato, ad es.il runtime fa il suo lavoro qui?
MODIFICARE:ok, quindi immagino che dovrei riformulare ciò che volevo dire con questa domanda.Fondamentalmente sto suggerendo che non c'è solo la magia del compilatore C#/CLR coinvolta nella costruzione del delegato, ma anche alcuni Studio visivo magia, dal momento che Intellisense lancia qualche nuova sintassi quando suggerisce gli argomenti del costruttore e ne nasconde persino uno (ad es.Reflector non utilizza questa sintassi e questo costruttore, del resto).
Mi chiedevo se questa affermazione è vera e se la sintassi dell'istanza della funzione ha un significato più profondo in C# o è solo un formato costante implementato dalla parte magica di Visual Studio per chiarezza (il che ha senso, poiché sembra C# non valido)?In breve, se implementassi Intellisense, dovrei fare qualche magia per i delegati o potrei costruire il suggerimento con qualche meccanismo intelligente?
MODIFICA FINALE:quindi, il consenso popolare è che questa è davvero magia VS.Vedere altri esempi (vedi il commento di Marc Gravell) di tale comportamento VS mi convince che sia così.
Soluzione
Il primo argomento viene risolto dal riferimento all'oggetto (o null
per metodi statici);nessuna magia lì.
Riguardo il secondo argomento, tuttavia: è un puntatore non gestito (int nativo);insomma, non c’è alternativa diretto Sintassi C# che può utilizzare questo costruttore: utilizza un'istruzione IL specifica (ldftn
) per risolvere la funzione dai metadati.Tuttavia, puoi usare Delegate.CreateDelegate
creare delegati attraverso la riflessione.Puoi anche usare IL emit (DynamicMethod
ecc.), ma non è divertente.
Altri suggerimenti
Per prima cosa definire il delegato (questo è il modo di Visual Studio conosce la firma del metodo di destinazione):
delegate void MyDelegate();
Poi si costruisce le istanze delegato in questo modo:
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 # preleva automaticamente il metodo di corrispondenza della firma del delegato.
Modifica: Quando Intellisense di Visual Studio vi mostra il suggerimento int () target
, sta mostrando la firma del C # i metodi che è possibile utilizzare. Il compilatore C # traduce la # rappresentazione C a IL. L'implementazione IL avrà un aspetto diverso, perché IL non è un linguaggio di stile C, e il compilatore C # fornisce zucchero sintattico per astrarre i dettagli di implementazione.
Questa è solo una supposizione, quindi non mi spara se sbaglio, ma credo che Intellisense è sempre la firma per il metodo di destinazione dal metodo Invoke
definito sul delegato. Riflettore mostra chiaramente che il metodo Invoke
su System.Action<T>
è:
[MethodImpl(0, MethodCodeType=MethodCodeType.Runtime)]
public virtual void Invoke(T obj);
Che è la stessa della suggestione firma offerto da Intellisense. La magia è Intellisense, quando avvistare un tipo delegato, guarda il metodo Invoke
e suggerisce un costruttore che prende un target che corrisponde esso.