2010-03-14 12 views
12

Während die Delegierten in C# und .NET im allgemeinen Inspektion, bemerkte ich einige interessante Fakten:Delegierte in .NET: Wie sind sie aufgebaut?

einen Delegierten in C# erstellen erstellt eine Klasse von MulticastDelegate mit einem Konstruktor abgeleitet:

.method public hidebysig specialname rtspecialname instance 
    void .ctor(object 'object', native int 'method') runtime managed { } 

Bedeutung dass es die Instanz und einen Zeiger auf die Methode erwartet. Doch die Syntax eines Delegaten in C# zu konstruieren lässt vermuten, dass es einen Konstruktor hat

new MyDelegate(int() target) 

wo ich int() als Funktion Beispiel erkennen kann (int *target() wäre ein Funktionszeiger in C++). Offensichtlich wählt der C# -Compiler die richtige Methode aus der durch den Funktionsnamen definierten Methodengruppe aus und baut den Delegaten auf. Die erste Frage wäre also: Woher nimmt der C# -Compiler (oder Visual Studio, um genau zu sein) diese Konstruktorsignatur? Ich habe keine besonderen Attribute oder etwas bemerkt, das einen Unterschied machen würde. Ist das eine Art von Compiler/Visual Studio Magie? Wenn nicht, ist die T (args) target Konstruktion in C# gültig? Ich habe es nicht geschafft, irgendetwas damit zu kompilieren, z. B .:

int() target = MyMethod;

ist ungültig, so ist irgendetwas mit MyMetod, z. Rufen Sie .ToString() darauf an (gut, das macht Sinn, da das technisch eine Methode Gruppe ist, aber ich stelle mir vor, es sollte möglich sein, eine Methode explizit durch Gießen, zB (int())MyFunction. So ist das alles rein Compiler Magie? mit Blick auf die Konstruktion durch Reflektor zeigt noch eine andere Syntax:

Func CS 1 $ 0000 $ = new Func (null, (IntPtr) Foo);

Dies steht im Einklang mit dem zerlegten Konstruktor Unterschrift, doch ist diese kompiliert nicht!

Eine letzte interessante Anmerkung ist, dass die Klassen Delegate und MulticastDelegate noch eine weitere Sätze von Konstrukteuren haben:

beträgt.Verfahren Familie hidebysig specialname rtspecialname Instanz Leere .ctor (Klasse System.Type Ziel, String ‚Methode‘) cil managed

Woher kommt der Übergang von einem Instanz- und Methodenzeiger zu einem Typ und einem String-Methodennamen? Kann dies mit den Schlüsselwörtern runtime managed in der benutzerdefinierten Delegiertenkonstruktorsignatur erklärt werden, d. H., Ob die Laufzeitumgebung hier ihren Zweck hat?

BEARBEITEN: ok, also denke ich, ich sollte neu formulieren, was ich mit dieser Frage sagen wollte. Im Grunde suggeriere ich, dass es nicht nur C# -Compiler/CLR-Magie bei der Delegiertenkonstruktion gibt, sondern auch einige magische Effekte, da Intellisense eine neue Syntax ausgibt, wenn sie die Konstruktorargumente vorschlägt und sogar einen davon versteckt (zB Reflector) Verwenden Sie diese Syntax und Konstruktor nicht für diese Angelegenheit).

Ich frage mich, ob diese Behauptung wahr ist, und ob die Syntax der Funktionsinstanz in C# eine tiefere Bedeutung hat oder nur ein konstantes Format, das von Visual Studio zur Klarheit implementiert wird (was sinnvoll erscheint, da es so aussieht ungültiges C#)? Kurz gesagt, sollte ich Intellisense implementieren, sollte ich etwas für Delegierte zaubern oder könnte ich den Vorschlag durch einen cleveren Mechanismus konstruieren?

FINAL EDIT: so ist der populäre Konsens, dass das in der Tat VS Magie ist. Wenn man andere Beispiele (siehe Marc Gravells Kommentar) eines solchen VS-Verhaltens sieht, überzeugt es mich, dass dies der Fall ist.

+0

'CS $ 1 $ 0000' ist ein generierter Decompiler, der niemals kompiliert wird. – Dykam

+1

Ich weiß, der Name ist nicht der Punkt hier, es ist die neue Konstruktion, die nicht kompiliert. –

+0

Yah, wollte nur darauf hinweisen. – Dykam

Antwort

11

Das erste Argument wird aus der Objektreferenz aufgelöst (oder null für statische Methoden); keine Magie dort.

Re das zweite Argument, jedoch - es ist ein nicht verwalteten Zeiger (native int); Kurz gesagt, gibt es keine Alternative direkte C# -Syntax, die diesen Konstruktor verwenden kann - es verwendet eine bestimmte IL-Anweisung (ldftn), um die Funktion von Metadaten aufzulösen. Sie können jedoch verwenden, um Delegaten über Reflektion zu erstellen. Sie können auch IL emit (DynamicMethod usw.) verwenden, aber es macht keinen Spaß.

+0

OK, also werden Methodenzeiger nicht von C# unterstützt, aber woher kommt die Syntax der "Funktionsinstanz"? Ich verstehe, von einem Konstruktor mit zwei Parametern zu einem einzelnen Parameter c-tor als Compilermagie zu gehen, aber wo kommt Visual Studio auf die Idee, einen einzelnen Parameterkonstruktor vorzuschlagen (ich kann es nirgendwo anders finden)? –

+1

@Saulius - weil das die Sprachspezifikation verlangt; es spricht nicht über die Konstruktoren, aber es besagt, dass die Syntax (zum Beispiel) 'new EventHandler (someObject.SomeMethod);' - Ich zähle eine "Eingabe" zu diesem, auch wenn der Compiler einige Magie auf es funktioniert erzeuge die IL. –

+0

@Marc Gravell: also wie wäre es mit meiner Behauptung über Visual Studio Magie (siehe die Bearbeitung des Beitrags)? Dieser Teil ist derjenige, der mich nervt - nur aus Neugier, natürlich :) –

0

Sie definieren zuerst die Delegierten (dies ist, wie Visual Studio die Signatur des Zielmethode weiß):

delegate void MyDelegate(); 

Dann bauen Sie Delegat-Instanzen wie folgt aus:

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# wählt automatisch die Methode, die der Signatur des Delegierten entspricht.

Bearbeiten: Wenn Visual Studio Intellisense zeigt Ihnen die Vorschlag, zeigt es Ihnen die Signatur von C# -Methoden, die Sie verwenden können. Der C# -Compiler übersetzt die C# -Darstellung in IL. Die IL-Implementierung wird anders aussehen, da IL keine Sprache im C-Stil ist und der C# -Compiler syntaktischen Zucker bereitstellt, um die Implementierungsdetails wegzuspulen.

+1

Ich denke, er meint aus einer niedrigen Perspektive. –

+0

Das ist der Punkt - die vorgeschlagene Syntax ist nicht gültig C#. –

+0

@Saulius: Das 'int() Ziel' an sich ist kein gültiges C#, aber wie sonst würden sie das Ziel in Intellisense darstellen? –

0

Dies ist nur eine Vermutung, also erschießt mich nicht, wenn ich falsch liege, aber ich denke, Intellisense erhält die Signatur für die Zielmethode aus der Invoke Methode, die auf dem Delegaten definiert ist. Reflector zeigt deutlich, dass die Invoke Methode auf System.Action<T> ist:

[MethodImpl(0, MethodCodeType=MethodCodeType.Runtime)] 
public virtual void Invoke(T obj); 

die die gleiche wie die Unterschrift Vorschlag von Intellisense angeboten wird. Die Magie ist, dass Intellisense beim Entdecken eines Delegattyps die Invoke-Methode betrachtet und einen Konstruktor vorschlägt, der ein entsprechendes Ziel übernimmt.

+0

Ja, die Signatur ist die der 'Invoke' Methode, aber die * Syntax * VS zeigt C# an! –

+0

@Saulius: Der von VS angezeigte Tooltip zeigt die bare ** Signatur ** der Zielmethode und nicht alle zusätzlichen Informationen, die von Ziel zu Ziel variieren würden (wie die Parameternamen und der Accessibility Modifier). Wie Marc betont hat, handelt es sich um einen Sonderfall, bei dem Intellisense etwas Pseudo-Code ausspuckt, anstatt es aus den Metadaten der Assembly auszulesen. – Rory