21.7 Delegateninstanzgleichheit
Die folgenden Regeln gelten für Ergebnisse, die vom Gleichheitsoperator (§7.9.8) und der Methode „object.Equals“ anonymer Methodendelegateninstanzen erzeugt werden.
l Wenn Delegateninstanzen aus der Auswertung semantisch identischer anonymer Methodenausdrücke mit demselben Satz erfasster externer Variablen resultieren, gelten sie als gleich (müssen aber nicht sein).
l Wenn Delegateninstanzen durch anonyme Methodenausdrücke mit unterschiedlicher Semantik oder mit unterschiedlichen Sätzen erfasster externer Variablen dargestellt werden, sind sie niemals gleich.
21.8 Explizite Zuweisung
Der explizite Zuweisungsstatus anonymer Methodenparameter ist der gleiche wie der benannter Methoden. Das heißt, Referenzparametern und Wertparametern werden explizit Anfangswerte zugewiesen, während Ausgabeparametern keine Anfangswerte zugewiesen werden müssen. Außerdem müssen Ausgabeparameter explizit zugewiesen werden, bevor die anonyme Methode normal zurückkehrt (§5.1.6).
Wenn die Steuerung an den Programmblock des anonymen Methodenausdrucks übergeben wird, ist der explizite Zuweisungsstatus der externen Variablen v derselbe wie der explizite Zuweisungsstatus von v vor dem anonymen Methodenausdruck. Das heißt, explizite Zuweisungen an externe Variablen werden vom Kontext des anonymen Methodenausdrucks geerbt. Innerhalb anonymer Methodenblöcke werden explizite Zuweisungen wie innerhalb gewöhnlicher Blöcke abgeleitet (§5.3.3).
Der explizite Zuweisungsstatus der Variablen v nach dem anonymen Methodenausdruck ist derselbe wie ihr expliziter Zuweisungsstatus vor dem anonymen Methodenausdruck.
Beispielsweise generiert
delegate bool Filter(int i); void F() { int max; // 错误,max没有明确赋值 Filter f = delegate(int n) { return n < max; } max = 5; DoWork(f); }
einen Fehler bei der Kompilierung, da max nicht explizit dort zugewiesen wird, wo die anonyme Methode deklariert ist. Beispiel
delegate void D(); void F() { int n; D d = delegate { n = 1; }; d(); //错误,n没有明确赋值 Console.WriteLine(n); }
generiert ebenfalls einen Fehler bei der Kompilierung, da die Zuweisung von n innerhalb der anonymen Methode keinen Einfluss auf den expliziten Zuweisungsstatus von n außerhalb der anonymen Methode hat.
21.9 Methodengruppenkonvertierung
Ähnlich wie bei der in §21.3 beschriebenen impliziten anonymen Methodenkonvertierung gibt es auch eine implizite Konvertierung von einer Methodengruppe (§7.1) in einen kompatiblen Delegattyp Konvertieren.
Wenn für eine gegebene Methodengruppe E und einen Delegatentyp D Delegatenerstellungsausdrücke der Form „new D(E)“ zulässig sind (§7.5.10.3 und §20.9.6), dann gibt es einen impliziten Pfad von E nach D Konvertierung, und das Ergebnis der Konvertierung entspricht genau dem neuen D(E).
Im folgenden Beispiel erstellt der Konstruktor
using System; using System.Windows.Forms; class AlertDialog { Label message = new Label(); Button okButton = new Button(); Button cancelButton = new Button();` public AlertDialog() { okButton.Click += new EventHandler(OkClick); cancelButton.Click += new EventHandler(CancelClick); ... } void OkClick(object sender, EventArgs e) { ... } void CancelClick(object sender, EventArgs e) { ... } }
zwei Delegateninstanzen mit new. Implizite Methodengruppenkonvertierungen ermöglichen eine Vereinfachung auf
public AlertDialog() { okButton.Click += OkClick; cancelButton.Click += CancelClick; ... }
Wie bei allen anderen impliziten und expliziten Konvertierungen können Konvertierungsoperatoren verwendet werden, um eine bestimmte Konvertierung explizit durchzuführen. Dazu kann das Beispiel
object obj = new EventHandler(myDialog.OkClick);
stattdessen wie folgt geschrieben werden.
object obj = (EventHandler)myDialog.OkClick;
Anonyme Methodenausdrücke der Methodenzusammensetzung können die Überladungsauflösung beeinflussen, sind jedoch nicht an der Typinferenz beteiligt. Weitere Einzelheiten finden Sie in §20.6.4.
21.10 Implementierungsbeispiel
In diesem Abschnitt werden mögliche Implementierungen anonymer Methoden in Form von Standard-C#-Komponenten beschrieben. Die hier beschriebene Implementierung basiert auf denselben Prinzipien, die auch der Microsoft C#-Compiler verwendet, ist jedoch keineswegs obligatorisch oder die einzig mögliche Implementierung.
Der letzte Teil dieses Abschnitts enthält mehrere Beispielcodes, die anonyme Methoden mit unterschiedlichen Eigenschaften enthalten. Für jedes Beispiel stellen wir eine entsprechende Transformation des Codes mithilfe eines eindeutigen Standard-C#-Konstrukts bereit. In diesen Beispielen wird davon ausgegangen, dass der Bezeichner D den folgenden Delegatentyp darstellt.
public delegate void D();
Die einfachste Form einer anonymen Methode ist die, die keine externen Variablen erfasst.
class Test { static void F() { D d = delegate { Console.WriteLine("test"); }; } }
Dieser Code kann in eine Delegate-Instanz konvertiert werden, die auf eine vom Compiler generierte statische Methode verweist, und der Code der anonymen Methode wird in die statische Methode eingefügt. ,
class Test { static void F() { D d = new D(__Method1); } static void __Method1() { Console.WriteLine("test"); } }
Im folgenden Beispiel verweist die anonyme Methode auf das Instanzmitglied davon.
class Test { int x; void F() { D d = delegate { Console.WriteLine(x); }; } }
Dies kann in eine vom Compiler generierte Instanzmethode konvertiert werden, die anonymen Methodencode enthält.
class Test { int x; void F() { D d = new D(__Method1); } void __Method1() { Console.WriteLine(x); } }
In diesem Beispiel erfasst die anonyme Methode eine lokale Variable.
class Test { void F() { int y = 123; D d = delegate { Console.WriteLine(y); }; } }
该局部变量的生存期现在至少必须延长到匿名方法委托的生存期为止。这可以通过将局部变量“提升(lifting)”为编译器生成的(compiler-generated)类的字段来完成。局部变量的实例化对应于创建一个编译器生成的类的实例,而访问局部变量将对应于访问编译器生成的类实例的一个字段。并且,匿名方法将成为编译器生成类的实例方法。
class Test { void F() { __locals1 = new __Locals1(); __locals1.y = 123; D d = new D(__locals1.__Method1); } class __Locals1 { public int y; public void __Method1() { Console.WriteLine(y); } } }
最后,如下匿名方法将捕获this,以及具有不同生存期的两个局部变量。
class Test { int x; void F() { int y = 123; for (int i = 0; i < 10; i++) { int z = i * 2; D d = delegate { Console.WriteLine(x + y + z); }; } } }
在这里,编译器将为每个语句块生成类,在这些语句块中局部变量将被捕获,而在不同块中的局部变量将会有独立的生存期。
__Locals2的实例,编译器为内部语句块生成的类,包含局部变量z和引用__Locals1实例的字段。__Locals1的实例,编译器为外部语句块生成的类,包含局部变量y和引用封闭函数成员的this的字段。通过这些数据结构,你可以通过__Locals2的一个实例到达所有被捕获的局部变量,并且匿名方法的代码可以作为那个类的实例方法而实现。
class Test { void F() { __locals1 = new __Locals1(); __locals1.__this = this; __locals1.y = 123; for (int i = 0; i < 10; i++) { __locals2 = new __Locals2(); __locals2.__locals1 = __locals1; __locals2.z = i * 2; D d = new D(__locals2.__Method1); } } class __Locals1 { public Test __this; public int y; } class __Locals2 { public __Locals1 __locals1; public int z; public void __Method1() { Console.WriteLine(__locals1.__this.x + __locals1.y + z); } } }
(匿名方法完)
以上就是C# 2.0 Specification(匿名方法)(二)的内容,更多相关内容请关注PHP中文网(www.php.cn)!