Heim > Artikel > Backend-Entwicklung > C# 2.0-Spezifikation (Generics 6)
Verbunden mit Generics 5
20.8 Ausdrücke und Anweisungen
Die Operationen einiger Ausdrücke und Anweisungen wurden für Generics geändert. In diesem Abschnitt werden diese Änderungen beschrieben.
20.8.1 Standardwertausdrücke
Standardwertausdrücke werden verwendet, um den Standardwert eines Typs zu erhalten (§5.2). Normalerweise wird für Typparameter ein Standardwertausdruck verwendet, denn wenn der Typparameter ein Werttyp oder ein Referenztyp ist, ist er möglicherweise noch nicht vorhanden. (Es findet keine Konvertierung vom Nulltyp in den Typparameter statt.)
primary-no-array-creation-expression:(基本无数组创建表达式:) … default-value-expression(默认值表达式) default-value-expression:(默认值表达式:) primary-expression . default (基本表达式 .default) predefined-type . default(预定义类型. default)
Wenn ein Basisausdruck in einem Standardwertausdruck verwendet wird und der Basisausdruck nicht in einen Typ unterteilt werden kann, dann a Es tritt ein Kompilierzeitfehler auf. Die in §7.5.4.1 beschriebenen Regeln gelten jedoch auch für Komponenten, die E.default bilden.
Wenn die linke Seite des Standardwertausdrucks zur Laufzeit anhand eines Referenztyps ausgewertet wird, wird das Ergebnis als Null in diesen Typ konvertiert. Wenn die linke Seite eines Standardwertausdrucks zur Laufzeit anhand eines Werttyps ausgewertet wird, ist das Ergebnis der Standardwert des Werttyps (§4.1.2).
Wenn der Typ ein Referenztyp oder Typparameter mit Klasseneinschränkungen ist, ist der Standardwertausdruck ein konstanter Ausdruck (§7.15). Darüber hinaus ist der Standardwertausdruck ein konstanter Ausdruck, wenn der Typ einer der folgenden ist: sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal oder bool.
20.8.2 Ausdrücke zur Objekterstellung
Der Typ allgemeiner Ausdrücke von Objekten kann ein Typparameter sein. Wenn ein Typparameter als Typ in einem Objekterstellungsausdruck angegeben wird, müssen die folgenden zwei Bedingungen erfüllt sein, andernfalls tritt ein Fehler bei der Kompilierung auf
Die eigentliche Parameterliste muss gelöscht werden
Muss angegeben werden für den Typparameter Konstruktoreinschränkungen der Form new()
führen Sie einen Objekterstellungsausdruck aus, indem Sie eine Instanz des Laufzeittyps erstellen, an den der Typparameter gebunden ist, und den Standardkonstruktor dieses Typs aufrufen. Laufzeittypen können Referenz- oder Werttypen sein.
20.8.3 Operatortypen
Der Operator „typeof“ kann für Typparameter verwendet werden. Das Ergebnis ist ein System.Type-Objekt des Laufzeittyps, das an den Typparameter gebunden ist. Der Operator typeof kann auch zum Konstruieren von Typen verwendet werden.
class X <T> { public static void PrintTypes() { Console.WriteLine(typeof(T).FullName); Console.WriteLine(typeof(X<X<T>>).FullName); } } class M { static void Main() { X<int>.PrintTypes(); } }
Das vorherige Programm wird wie folgt gedruckt.
System.Int32 X<X<Sytem.Int32>> Typeof运算符不能用于没有指定类型实参的泛型类型声明的名字。 class X<T>{…} class M { static void Main() { Type t = typeof(X); //错误,X需要类型实参 } }
20.8.4 Referenz-Gleichheitsoperator
Wenn T durch eine Klassenbeschränkung eingeschränkt ist, kann der Referenztyp-Gleichheitsoperator zum Vergleichen der Werte des Typs verwendet werden Parameter T.
Die Verwendung des Referenztyp-Gleichheitsoperators ermöglicht den einfachen Vergleich der tatsächlichen Parameter des Typparameters T mit anderen tatsächlichen Parametern, die null sind, selbst wenn T keine Klassenbeschränkungen aufweist. Wenn T zur Laufzeit ein Werttyp ist, ist das Ergebnis des Vergleichs falsch.
Das folgende Beispiel prüft, ob der tatsächliche Parameter eines nicht eingeschränkten Typparametertyps null ist.
class C<T> { void F(T x) { if(x==null) thow new ArgumentNullException(); … } }
Auch wenn T einen Werttyp darstellen kann, ist das Konstrukt x==null zulässig, und wenn T ein Werttyp ist, wird sein Ergebnis einfach als falsch definiert.
20.8.5 is运算符
在开放类型上的is运算符操作遵循通常的规则(§7.9.9)。如果e或T的编译时类型是一个开放类型,那么在运行时对于e和T将总是执行动态类型检查。
20.8.6as运算符
只要T有一个类约束,类型参数T可被用在as运算符的右边。这种限制是需要的,因为值null可能被作为运算符的结果返回。
class X { public T F<T>(object o) where T:Attribute { return o as T; //ok,T有一个类约束 } public T G<T>(object o) { return o as T; //错误,T没有约束 }
}
在as运算符(§7.9.10)的当前规范中,对于表达式e as T最后一点表明,如果从e的编译时类型到T,不存在有效的显式引用转换,将会出现编译时错误。对于泛型,这条规则稍微作了修改。如果E的编译时类型或T是一个开放类型,在这种情况下将不会出现编译时错误;相反,运行时检查将会执行。
20.8.7异常语句
对于开放类型,throw(§8.9.5)和try(§8.10)的通常规则是适用的。
只要类型参数具有System.Exeption异常(或子类具有)作为类约束,那么throw语句可以被用作其类型有一个类型参数给定的表达式。
只要类型参数System.Exception(或子类子类具有)作为类约束,那么在catch语句中的命名的类型可能是一个类型参数。
20.8.8 lock语句
lock语句可以被用作其类型由一个类型参数给定的表达式。如果表达式的运行时类型是一个值类型,lock将没有效果(因为对于装箱值不能有任何其他的引用)。
20.8.9 using 语句
using 语句(§8.13)遵循通常的规则:表达式必须被隐式的转换到System.IDisposable。如果类型参数通过System.IDisposable而约束,那么该类型的表达式可以使用using 语句。
20.8.10 foreach语句
给定如下形式的foreach语句
foreach(ElementType element in collection) statement
如果集合表达式是一个没有实现集合模式的类型,但为每个类型T实现了构造接口System.Collections.Generic.IEnumerable8742468051c85b06f0a0af9e3e506b5c,那么foreach语句的扩展是
IEnumerator<T> enumerator = ((IEnuemrable<T>)(collection).GetEnumerator(); try { where (enumerator.MoveNext()){ ElementType element = (ElementType)enumerator.Current; statement; } } finally{ enumerator.Dispose(); }
20.9查找规则修订
泛型修改了用于查找和绑定名字的某些基本规则。下面几节在考虑泛型的情况下,重新叙述了所有的基本名字查找规则。
20.9.1命名空间和类型名字
Der folgende Inhalt kann §3.8 ersetzen.
Es gibt mehrere Kontexte in C#-Programmen, die die Angabe von Namespaces oder Typnamen erfordern. Jede Namensform kann aus einem oder mehreren durch „.“ getrennten Bezeichnern bestehen.
Namespace-Name: (Namespace-Name:)
Namespace-oder-Typname (Namespace- oder Typname)
Typname: (Typname:)
Namespace-oder-Typ-Name (Namespace- oder Typname)
Namespace-oder-Typname: (Namespace- oder Typname:)
Bezeichnertyp-Argumentliste opt (Bezeichnertyp-Argumentliste optional)
Namespace-oder- Typname. Bezeichnertyp-Argumentliste opt (Namespace oder Typname. Bezeichnertypargumentliste optional)
Der Namespacename ist der Namespacename oder Typname, der auf den Namespace verweist (namespace-or-type- Name). Siehe die unten beschriebenen Entscheidungen. Der Namespace- oder Typname eines Namespace-Namens muss auf einen Namespace verweisen, andernfalls tritt ein Fehler bei der Kompilierung auf. Ein Namespace-Name darf keine Typargumente enthalten (nur Typen können Typargumente haben).
Ein Typname ist ein Namespace- oder Typname, der auf einen Typ verweist. Siehe die unten beschriebenen Entscheidungen. Der Namespace oder Typname eines Typnamens muss auf einen Typ verweisen, andernfalls tritt ein Fehler bei der Kompilierung auf.
Die Bedeutung eines Namespace- oder Typnamens wird wie folgt bestimmt.
Wenn der Namespace oder Typname die Form I oder If53db7e3537772cbf9ba1634d2b17fe6 hat, wobei I ein einzelner Bezeichner und f53db7e3537772cbf9ba1634d2b17fe6 eine optionale Typargumentliste ist.
– Wenn ein Namespace oder Typname in einer generischen Methodendeklaration erscheint und diese Deklaration einen von I benannten Typparameter ohne eine angegebene Typargumentliste enthält, dann verweist der Namespace oder Typname auf den Typparameter.
– Andernfalls, wenn ein Namespace oder Typname in einer Typdeklaration erscheint, dann für jede Instanz des Typs T (f53db7e3537772cbf9ba1634d2b17fe620.1.2), beginnend mit dem Instanztyp dieser Typdeklaration, und fährt mit jeder umschließenden Klassen- oder Strukturtypdeklaration (falls vorhanden) fort
u Wenn keine Deklaration angegeben ist, die einen Typparameter T enthält, der den durch I angegebenen Namen enthält, und keine Typargumentliste vorhanden ist, dann verweist der Namespace oder Typname darauf Typparameter.
u Andernfalls, wenn I der Name eines zugänglichen Mitglieds von T ist und wenn dieses Mitglied ein Typ mit einer passenden Anzahl von Typparametern ist, dann bezieht sich der Namespace oder Typname auf den Typ T.I oder Typ T .I0c4640bb3be93baace618a998f381d1c. Beachten Sie, dass bei der Bestimmung der Bedeutung von Namespace- oder Typnamen untypisierte Mitglieder (Konstanten, Felder, Methoden, Eigenschaften, Indexer, Operatoren, Instanzkonstruktoren, Destruktoren und statische Konstruktoren) und solche mit unterschiedlicher Anzahl von Typparametern ignoriert werden.
– Andernfalls werden für jeden Namespace N, beginnend mit einem darin vorkommenden Namespace oder Typnamen, über jeden umschließenden Namespace (sofern vorhanden) und endend mit dem globalen Namespace, die folgenden Schritte berechnet, bis die Entität gefunden wird .
u Wenn I ein Name in einem Namespace in N ist und keine Typargumentliste angegeben ist, dann bezieht sich der Namespace oder Typname auf diesen Namespace.
u Andernfalls, wenn I der Name eines zugänglichen Typs in N mit einer passenden Anzahl von Typargumenten ist, dann bezieht sich der Namespace oder Typname auf einen Typ, der mit den angegebenen Typargumenten erstellt wurde.
u Andernfalls, wenn der Namespace oder Typname an einer Position erscheint, die von einer Namespace-Deklaration von N umschlossen ist
– wenn die Namespace-Deklaration eine using-Alias-Direktive mit einem durch I angegebenen Namen enthält und I über einen Import-Namensraum verfügt oder Typ, und es ist keine Argumentliste angegeben, dann bezieht sich der Namespace oder Typname auf diesen Namespace oder Typ
– andernfalls, wenn der durch die using-Namespace-Direktive der Namespace-Deklaration importierte Namespace zufällig einen Namespace mit der Angabe „I“ enthält Wenn Sie einen benannten Typ angeben, der mit der Anzahl der Typparameter übereinstimmt, bezieht sich der Namespace oder Typname auf den Typ, der aus den angegebenen Typargumenten erstellt wurde.
– Andernfalls ist der Namespace oder Typname mehrdeutig und führt zu einem, wenn der durch die using-Namespace-Direktive in der Namespace-Deklaration importierte Namespace mehr als einen Typ mit dem angegebenen Namen enthält, der mit der Anzahl der Typargumente übereinstimmt Fehler.
- Andernfalls ist der Namespace oder Typname undefiniert und es tritt ein Fehler bei der Kompilierung auf.
l Ansonsten hat der Namespace oder Typname die Form N.I oder N.If53db7e3537772cbf9ba1634d2b17fe6, wobei N ein Namespace oder Typname ist, I ein Bezeichner ist und < ;A1,…,AN> ist eine Liste optionaler Typargumente. N wird zunächst als Namensraum- oder Typname ermittelt. Wenn die Entscheidung von N fehlschlägt, tritt ein Fehler bei der Kompilierung auf. Andernfalls wird N.I oder N.If53db7e3537772cbf9ba1634d2b17fe6 entschieden.
– Wenn N auf einen Namespace verweist und I ein in N eingebetteter Namespace-Name ist und keine Typargumentliste angegeben ist, dann bezieht sich der Namespace oder Typname auf den eingebetteten Namespace.
– Andernfalls, wenn N auf einen Namespace verweist und I der Name eines Typs ist, auf den in N mit einer entsprechenden Anzahl von Typargumenten zugegriffen werden kann, dann bezieht sich der Namespace oder Typname auf den Typ, der aus den angegebenen Typargumenten erstellt wurde.
– Andernfalls, wenn N auf eine Klasse oder einen Strukturtyp verweist und I der Name eines in N eingebetteten zugänglichen Typs mit passenden Typparametern ist, wird der Namespace oder die Typnamenreferenz mit den angegebenen Argumenten dieses Typs erstellt.
– Andernfalls ist N.I ein ungültiger Namespace-Name und es tritt ein Fehler bei der Kompilierung auf.
20.9.2 Mitgliedersuche
Der folgende Inhalt kann §7.3 ersetzen
Die Mitgliedersuche ist ein Prozess, der den Typ anhand seiner Bedeutung im Kontext bestimmt. Innerhalb eines Ausdrucks kann die Mitgliedersuche als einfache Namensauswertung oder als Mitgliederzugriff erfolgen (§20.9.4).
Die Mitgliedersuche des Namens N im Typ T wird nach den folgenden Regeln bestimmt.
Zuerst wird eine Menge zugänglicher Mitglieder mit dem Namen N bestimmt.
– Wenn T ein Typparameter ist, dann wird in jedem Typ, der als Klasseneinschränkung oder Schnittstelleneinschränkung von T angegeben ist, zusammen mit der Menge der benannten Mitglieder von N im Objekt diese Menge als Zugriffsmitglieder der Föderation bezeichnet .
- Ansonsten besteht diese Menge aus allen benannten zugänglichen Mitgliedern von N in T, einschließlich geerbter Mitglieder und benannter zugänglicher Mitglieder von N im Objekt. Wenn T ein konstruierter Typ ist, wird die Menge der Mitglieder durch Ersetzen der Typargumente wie in §20.5.4 beschrieben ermittelt. Mitglieder, die den Override-Modifikator enthalten, werden aus der Sammlung ausgeschlossen.
Mitglieder, die durch andere Mitglieder ausgeblendet sind, werden dann aus dem Set entfernt. Für jedes Mitglied S.M in einer Menge, wobei S der Typ ist, in dem M deklariert wird, gelten die folgenden Regeln
– Wenn M eine Konstante, ein Feld, eine Eigenschaft, ein Ereignis oder ein Aufzählungsmitglied ist, dann in Alle Mitglieder in Basisklassen von S deklarierte Dateien werden aus dieser Sammlung entfernt.
- Wenn M eine Typdeklaration ist, werden alle Nichttypdeklarationen in der Basisklasse von S aus der Menge entfernt und alle Typdeklarationen von M mit der gleichen Anzahl von Typparametern wie S im Basistyp deklariert. wird aus der Sammlung entfernt.
- Wenn M eine Methode ist, werden alle in der Basisklasse von S deklarierten Nicht-Methoden-Mitglieder aus dieser Menge entfernt, und alle Methoden mit derselben Signatur von M, die im Basistyp von S deklariert sind, werden aus dieser Menge entfernt diese Sammlung.
Dann werden durch Klassenmitglieder ausgeblendete Schnittstellenmitglieder aus der Sammlung entfernt. Dieser Schritt ist nur gültig, wenn T ein Typparameter ist und T Klasseneinschränkungen und mehrere Schnittstelleneinschränkungen aufweist. Für jedes Mitglied S.M in der Sammlung, wobei S der Typ ist, in dem M deklariert ist, gelten die folgenden Regeln, wenn S eine Klassendeklaration und kein Objekt ist:
– wenn M eine Konstante, ein Feld oder eine Eigenschaft ist , Ereignis, Aufzählungsmitglied oder Typdeklaration, dann werden alle in der Schnittstellendeklaration deklarierten Mitglieder aus diesem Satz entfernt.
- Wenn M eine Methode ist, werden alle im Schnittstellentyp deklarierten Nicht-Methoden-Mitglieder aus dieser Menge entfernt, und alle Methoden mit derselben Signatur von M, die in der Schnittstelle wie S deklariert ist, werden aus dieser Menge entfernt . Aus dieser Sammlung entfernt.
Abschließend wird nach dem Löschen der ausgeblendeten Mitglieder das Ergebnis der Suche ermittelt
- Wenn eine Sammlung aus einem einzelnen Mitglied besteht, nicht aus einem Typ oder einer Methode, dann ist dieses Mitglied das Ergebnis der Suche.
- Andernfalls, wenn der Satz nur Methoden enthält, ist dieser Methodensatz das Ergebnis der Suche.
– Andernfalls, wenn der Satz nur Typdeklarationen enthält, ist dieser Satz von Typdeklarationen im Ergebnis der Mitgliedersuche.
- Andernfalls ist die Suche mehrdeutig und es tritt ein Fehler bei der Kompilierung auf.
Bei Typen handelt es sich bei der Elementsuche in Schnittstellen nicht um Typparameter und Member-Lookups von Schnittstellen, sondern ausschließlich um Einzelvererbung (jede Schnittstelle in der Vererbungskette hat genau null oder eine direkte Basisschnittstelle), Lookup Die Wirkung der Regel ist nur das Abgeleitete Mitglieder verbergen Basisklassenmitglieder mit demselben Namen und derselben Signatur. Diese Einzelvererbungssuche ist sehr explizit. Mögliche Unklarheiten bei der Mitgliedersuche ergeben sich aus der Mehrfachvererbungsschnittstelle, die in §13.2.5
20.9.3 Einfache Namen
beschrieben wirdDer folgende Inhalt kann §7.5.2 ersetzen.
Ein einfacher Name besteht aus einem Bezeichner, gefolgt von einer optionalen Liste von Typparametern.
simple-name: (einfacher Name:)
identifier type-argument-list opt (identifier type argument list optional)
Für einfache Namen der Form I oder If53db7e3537772cbf9ba1634d2b17fe6 , wobei I ein Bezeichner ist und If53db7e3537772cbf9ba1634d2b17fe6 eine Liste optionaler Typargumente ist, die wie folgt berechnet und klassifiziert werden können.
Wenn der einfache Name innerhalb eines Blocks erscheint und der lokale Variablendeklarationsraum des Blocks eine lokale Variable oder einen lokalen Parameter mit dem durch I angegebenen Namen enthält, dann bezieht sich der einfache Name auf die lokale Variable und den lokalen Parameter und fungiert als eine Variable und klassifiziert werden. Wenn eine Typargumentliste angegeben wird, tritt ein Fehler bei der Kompilierung auf.
Wenn der einfache Name im Hauptteil einer generischen Methodendeklaration erscheint und diese Deklaration einen mit I benannten Typparameter enthält, verweist der einfache Name auf diesen Typparameter. Wenn nur die Typargumentliste angegeben ist, ist dies der Fall Es ist ein Fehler bei der Kompilierung aufgetreten.
Andernfalls fahren Sie für jede Instanz des Typs T, die mit einem Instanztyp beginnt, der von der direkt umschließenden Klasse, Struktur oder Aufzählung deklariert wird, mit dem Instanztyp fort, der von jeder umschließenden äußeren Klasse oder Struktur (sofern vorhanden) deklariert wird.
– Wenn die Deklaration von T einen durch I benannten Typparameter enthält, bezieht sich der einfache Name auf diesen Typparameter. Wenn eine Typargumentliste angegeben wird, tritt ein Fehler bei der Kompilierung auf.
- Andernfalls, wenn eine Member-Suche von I in T eine Übereinstimmung erzeugt
u Wenn T ein Instanztyp einer direkt umschließenden Klasse oder eines Strukturtyps ist und die Suche eine oder mehrere Methoden identifiziert, ist das Ergebnis a match with Die diesem Ausdruck zugeordnete Methodengruppe. Wenn eine Typargumentliste angegeben ist, wird sie in einem generischen Methodenaufruf verwendet (§20.6.3).
u Wenn T ein Instanztyp einer direkt umschließenden Klasse oder eines Strukturtyps ist, wenn die Suche ein Instanzmitglied identifiziert und die Referenz innerhalb eines Blocks eines Instanzkonstruktors, einer Instanzmethode oder eines Instanzaccessors auftritt, ist das Ergebnis Das Gleiche gilt für den Mitgliederzugriff des .I-Formulars. Wenn ein Typargument angegeben wird, tritt ein Fehler bei der Kompilierung auf.
u Ansonsten ähnelt das Ergebnis einem Mitgliederzugriff der Form T.I oder T.If53db7e3537772cbf9ba1634d2b17fe6. In diesem Fall wäre es ein Fehler bei der Kompilierung, wenn auf ein Instanzmitglied mit seinem einfachen Namen verwiesen würde.
Andernfalls fahren Sie für Namespace N mit jedem Namespace fort, in dem der einfache Name vorkommt, fahren Sie mit jedem umschließenden Namespace (falls vorhanden) fort und enden Sie mit dem globalen Namespace. Die folgenden Schritte werden berechnet, bis eine Entität entsteht befindet.
– Wenn I der Name eines Namespace in N ist und keine Typargumentliste angegeben ist, verweist der einfache Name auf diesen Namespace.
– Andernfalls, wenn I der Name eines zugänglichen Typs in N mit einer passenden Anzahl von Typargumenten ist, bezieht sich der einfache Typ auf diesen Typ, der aus den angegebenen Typargumenten erstellt wurde.
u Wenn die Namespace-Deklaration eine using-Alias-Direktive enthält, die einem durch I angegebenen Namen zugeordnet ist, wobei I ein importierter Namespace oder Typ ist und keine Typargumentliste angegeben ist, dann bezieht sich der einfache Name auf den Namespace oder Typ.
u Andernfalls, wenn der durch die using-Namespace-Direktive in der Namespace-Deklaration importierte Namespace zufällig einen durch I benannten Typ enthält, der mit der Anzahl der Typargumente übereinstimmt, wird aus den angegebenen Typargumenten eine einfache Namensreferenz erstellt.
u Andernfalls, wenn der durch die using-Namespace-Direktive der Namespace-Deklaration importierte Namespace mehrere Typen enthält, deren von I angegebene Namen mit der Anzahl der Typargumente übereinstimmen, sind die einfachen Namen mehrdeutig und führen zu einem Fehler bei der Kompilierung.
l Andernfalls ist der durch den einfachen Namen angegebene Name undefiniert und führt zu einem Fehler bei der Kompilierung.
20.9.4 Mitgliederzugang
Der folgende Inhalt kann §7.5.4 ersetzen.
Der Zugriff auf Mitglieder besteht aus einem Basisausdruck oder einem vordefinierten Typ, gefolgt von einem „.“-Zeichen, gefolgt von einem Bezeichner und einer optionalen Liste von Typargumenten.
member-access: (member-access:)
primärer-ausdruck . list opt (vordefinierter Typ. Argumentliste für Bezeichnertyp optional) Vordefinierter Typ: einer der folgenden
bool byte char decimal double float int long
object sbyte short string uint ulong ushort
Für Mitglied Zugriff der Form E.I oder E.If53db7e3537772cbf9ba1634d2b17fe6, wobei E ein Basisausdruck oder vordefinierter Typ, I ein Bezeichner und f53db7e3537772cbf9ba1634d2b17fe6 ist. Die Liste der ausgewählten Typargumente wird wie folgt berechnet und sortiert.
Wenn E ein Namespace und I der Name eines verschachtelten Namespace in E ist und keine Typargumente angegeben sind, dann ist das Ergebnis dieser Namespace.
Wenn E ein Namespace ist, I der Name eines in E zugänglichen Typs ist und E mit der Anzahl der Typparameter übereinstimmt, dann ist das Ergebnis der aus den angegebenen Typargumenten erstellte Typ.
Wenn E ein vordefinierter Typ oder ein als Typ klassifizierter Basisausdruck ist, wenn E kein Typparameter ist und eine Elementsuche von I in E eine Übereinstimmung ergibt, wird E.I wie folgt ausgewertet und klassifiziert.
– Wenn ich eine oder mehrere Typdeklarationen identifiziere, wird diese Typdeklaration anhand der gleichen Anzahl (möglicherweise null) Typargumenten bestimmt, die in den Typargumenten angegeben wären. Das Ergebnis ist ein Typ, der aus den angegebenen Typargumenten erstellt wird. Wenn die Typdeklaration nicht mit der Anzahl der Typparameter übereinstimmt, tritt ein Fehler bei der Kompilierung auf.
– Wenn ich eine oder mehrere Methoden identifiziere, ist das Ergebnis eine Methodengruppe ohne zugehörigen Instanzausdruck. Wenn eine Typargumentliste angegeben ist, wird diese in generischen Methodenaufrufen verwendet (§20.6.3).
– Wenn ich eine statische Eigenschaft, ein statisches Feld, ein statisches Ereignis, eine Konstante oder ein Enumerationsmitglied identifiziere, tritt ein Fehler bei der Kompilierung auf, wenn eine Typargumentliste angegeben wird.
– Wenn ich eine statische Eigenschaft identifiziere, ist das Ergebnis ein Eigenschaftszugriff mit einem nicht zugeordneten Instanzausdruck.
- Wenn ich ein statisches Feld identifiziere
u Wenn das Feld schreibgeschützt ist und die Referenz außerhalb des statischen Konstruktors der Klasse oder Struktur erfolgt, wird das Feld hier deklariert. Dann ist das Ergebnis ein Wert, der der Wert des statischen Feldes I in E ist.
u Ansonsten ist das Ergebnis eine Variable, also das statische Feld I in E.
- Wenn I ein statisches Ereignis identifiziert
u Wenn die Referenz in der Klasse oder Struktur auftritt, in der das Ereignis deklariert ist, und das Ereignis ohne Ereignis-Accessor-Deklaration (§10.7) deklariert wird, dann ist E.I behandelt, als wäre ich ein statisches Feld.
u Andernfalls ist das Ergebnis ein Ereigniszugriff auf einen nicht zugeordneten Instanzausdruck.
- Wenn ich eine Konstante identifiziere, ist das Ergebnis der Wert, der der Wert der Konstante ist.
- Wenn ich ein Aufzählungsmitglied identifiziere, ist das Ergebnis ein Wert, der dem Wert des Aufzählungsmitglieds entspricht.
– Andernfalls ist E.I eine ungültige Mitgliedsreferenz und verursacht einen Fehler bei der Kompilierung.
Wenn E ein Eigenschaftszugriff, Indexerzugriff, eine Variable oder ein Wert vom Typ T ist und eine Elementsuche von I in T eine Übereinstimmung ergibt, wird E.I wie folgt berechnet und klassifiziert.
– Wenn E zunächst eine Eigenschaft oder ein Indexerzugriff ist, wird der Wert der Eigenschaft oder des Indexerzugriffs ermittelt (§7.1.1) und E wird als Wert neu klassifiziert.
- Wenn ich eine oder mehrere Methoden identifiziere, ist das Ergebnis eine Methodengruppe mit einem zugehörigen Instanzausdruck von E. Wenn eine Typargumentliste angegeben ist, wird diese in generischen Methodenaufrufen verwendet (§20.6.3).
– Wenn ich eine Instanzeigenschaft, ein Instanzfeld oder ein Instanzereignis identifiziere, wird ein Fehler bei der Kompilierung generiert, wenn eine Typargumentliste angegeben wird.
– Wenn ich eine Instanzeigenschaft identifiziere, ist das Ergebnis ein Instanzausdruck mit einem zugehörigen E .
- Wenn T ein Klassentyp ist und I ein Instanzfeld des Klassentyps identifiziert
u Wenn der Wert von E null ist, wird eine System.NullReferenceException ausgelöst.
u Andernfalls, wenn das Feld schreibgeschützt ist und die Referenz außerhalb eines Instanzkonstruktors der Klasse erfolgt, in der das Feld deklariert ist, ist das Ergebnis der Wert, der dem Wert von I in dem von E referenzierten Objekt entspricht.
u Andernfalls ist das Ergebnis die Variable, bei der es sich um das Feld I in dem von E referenzierten Objekt handelt.
- wenn T ein Strukturtyp ist und I ein Instanzfeld dieses Strukturtyps identifiziert
u wenn E ein Wert ist oder wenn das Feld schreibgeschützt ist und die Referenz im Instanzkonstruktor der Struktur in erscheint andernfalls ist das Ergebnis ein Wert, d. h. der Wert von Feld I in der durch E gegebenen Strukturinstanz.
u Ansonsten ist das Ergebnis eine Variable, also Feld I in der Instanz der durch E angegebenen Struktur;
- wenn I ein Instanzereignis identifiziert
u wenn die Referenz in der Klasse oder Struktur erscheint in dem das Ereignis innerhalb deklariert wurde und das Ereignis ohne eine Ereignis-Accessor-Deklaration deklariert wird, wird E.I so behandelt, als wäre ich ein Instanzfeld.
u Andernfalls ist das Ergebnis ein Instanzausdruck mit einem zugehörigen E.
Andernfalls ist E.I eine ungültige Mitgliedsreferenz und verursacht einen Fehler bei der Kompilierung.
20.9.5 Methodenaufruf
Der folgende Inhalt kann den Teil zur Kompilierungszeitverarbeitung ersetzen, der den Methodenaufruf in §7.5.5.1 beschreibt.
Verarbeitung von Methodenaufrufen zur Kompilierungszeit in Form von M(A), wobei M eine Methodengruppe ist (kann eine Typargumentliste enthalten) und A eine optionale Argumentliste ist, bestehend aus den folgenden Schritten.
Ein Kandidatensatz von Methodenaufrufen wird erstellt. Für jede Methode F
, die der Methodengruppe M zugeordnet ist – wenn F nicht generisch ist, ist F ein Kandidat, wenn
u M keine Typargumentliste hat und
u für A (§7.4. 2.1) gilt F.
- Wenn F ein Generikum ist und M keine Typargumentliste hat, ist F ein Kandidat, wenn und
u Sobald die abgeleiteten Typargumente die entsprechenden Methodentypparameter ersetzen, ist die Parameterliste von F auf A anwendbar, und
u Nach dem Ersetzen der Typargumente ist die Parameterliste von F so anwendbar wie möglich. Als F deklarierte nicht generische Methoden desselben Typs unterscheiden sich in ihrer erweiterten Form (§7.4.2.1).
- Wenn F ein Generikum ist und M eine Typargumentliste enthält, ist F ein Kandidat für Parameter und
u Sobald die Typargumente die entsprechenden Methodentypparameter ersetzen, ist die Parameterliste von F auf A anwendbar ( §7.4.2.1).
Die Menge der Kandidatenmethoden wird so reduziert, dass sie nur Methoden enthält, die von tief abgeleiteten Typen abgeleitet sind: Für jede C.F-Methode in der Menge ist C der Typ, in dem F deklariert ist, und der Basistyp von C ist in deklariert Alle Methoden von werden aus der Sammlung entfernt.
Wenn die Ergebnismenge der Kandidatenmethode leer ist, ist keine anwendbare Methode vorhanden und es tritt ein Fehler bei der Kompilierung auf. Wenn die Kandidatenmethode nicht im gleichen Typ deklariert ist, ist der Methodenaufruf mehrdeutig und es tritt ein Fehler bei der Kompilierung auf (letzterer Fall ist nur für eine Methode in einer Schnittstelle mit mehreren direkten Basisschnittstellen möglich), wie in beschrieben §13.2.5).
Die beste Methode aus der Menge der Kandidatenmethoden wird mithilfe der Überlastungsentscheidungsregel (§7.4.2) identifiziert. Wenn eine einzelne beste Methode nicht identifiziert werden kann, ist der Methodenaufruf nicht eindeutig und generiert einen Fehler bei der Kompilierung. Bei der Überladungsauflösung werden Parameter einer generischen Methode berücksichtigt, nachdem die entsprechenden Methodentypparameter durch Typargumente (bereitgestellt oder abgeleitet) ersetzt wurden.
– Die Methode ist im Kontext einer Methodengruppe gültig: Wenn es sich bei der Methode um eine statische Methode handelt, muss die Methodengruppe vom einfachen Namen oder abgeleitet werden Zugang für Mitglieder. Wenn die beste Methode eine Instanzmethode ist, muss auf die Methodengruppe über eine Variable oder einen Wert oder einen Basiszugriff zugegriffen werden können, der vom einfachen Namen oder Mitgliedszugriff abgeleitet ist. Wenn keine dieser Anforderungen erfüllt ist, tritt ein Fehler bei der Kompilierung auf.
– Wenn die beste Methode eine generische Methode ist, werden die Typargumente (bereitgestellt oder abgeleitet) anhand der für die generische Methode deklarierten Einschränkungen überprüft. Wenn ein Typargument die Einschränkungen des entsprechenden Typparameters nicht erfüllt, wird ein Fehler bei der Kompilierung generiert.
Sobald die Methode gemäß den vorherigen Schritten ausgewählt und überprüft wurde, wird der eigentliche Laufzeitaufruf gemäß den Aufrufregeln für Funktionsmitglieder in §7.4 behandelt.
20.9.6 Erstellungsausdruck delegieren
Der folgende Inhalt kann den Verarbeitungsteil zur Kompilierungszeit des Delegatenerstellungsausdrucks in §7.5.10.3 ersetzen.
Die Kompilierzeitverarbeitung von Delegatenerstellungsausdrücken in Form von neuem D(E), wobei D ein Delegattyp und E ein Ausdruck ist, besteht aus den folgenden Schritten.
Wenn E eine Methodengruppe
ist – entsprechend einem Methodenaufruf der Form E(A), wird ein einzelner Methodenaufruf ausgewählt.
u Der Parametertyp und der Modifikator (ref oder out) von D werden als Argumenttyp und Modifikator der Argumentliste A verwendet.
u Konvertierungen werden bei anwendbaren Tests und Typrückschlüssen nicht berücksichtigt. In Fällen, in denen eine implizite Konvertierung ausreichend ist, sind die Typanforderungen identisch.
u Überlastungsentscheidungsschritt wird nicht ausgeführt. Stattdessen muss die Kandidatenmenge genau eine D-kompatible Methode enthalten (und dann Typargumente anstelle von Typparametern verwenden), und diese Methode wird zur Methode, auf die der neu erstellte Delegat verweist. Wenn keine passende Methode vorhanden ist oder wenn mehrere passende Methoden vorhanden sind, tritt ein Fehler bei der Kompilierung auf.
– Wenn die ausgewählte Methode eine Instanzmethode ist, bestimmt der mit E verknüpfte Instanzausdruck das Zielobjekt des Delegaten.
- Das Ergebnis ist ein Wert vom Typ D, der sich auf die ausgewählte Methode und den neu erstellten Delegaten des Zielobjekts bezieht.
Andernfalls ist E ein Wert vom Delegattyp
– D und E müssen kompatibel sein, andernfalls tritt ein Fehler bei der Kompilierung auf.
- Das Ergebnis ist ein Wert vom Typ D, d. h. ein neu erstellter Delegat, der auf dieselbe Anrufliste wie E verweist.
Andernfalls ist der Delegatenerstellungsausdruck ungültig und es tritt ein Fehler bei der Kompilierung auf.
20.10 Änderungen der Rechtsverschiebungssyntax
Generika verwenden „38e34801438fe1cec3be71c935a6c83e“-Zeichen, um Typparameter und Typargumente zu trennen (ähnlich der Vorlagensyntax von C++). Konstruierte Typen können manchmal verschachtelt sein, wie zum Beispiel Listdd77601bbe5f7db04992ba7b2f8dd60c>, aber es gibt einige subtile Syntaxprobleme bei der Verwendung dieses Konstrukts: Der Lexer kombiniert die letzten beiden Token dieses Konstrukts, „>>“ (rechts). Verschiebungsoperator), anstatt die beiden von der Syntax geforderten „>“-Tags zu erzeugen. Obwohl eine mögliche Lösung darin besteht, zwischen den beiden „>>“ Leerzeichen einzufügen, ist dies ebenfalls umständlich und verwirrend und erhöht in keiner Weise die Einfachheit des Programms.
Um die lexikalische Einfachheit dieser neutralen Konstrukte beizubehalten, wurden die Markierungen „>>“ und „>>=" aus dem Lexikon entfernt und durch Rechtsverschiebungs- und Rechtsverschiebungszuweisungsproduktionen ersetzt.
Operator oder Interpunktion: einer von
{ } [ ] ( ) , : ;
+ - * / % & | ^ ! |. == ->
!= b207bb093495751f3e536c5eb7122819= += -= *= /= %= &= |=
^= 38c296648a06d27828cb2c2bf85e3492 >
Rechtsverschiebungszuweisung: (Rechtsverschiebungszuweisung)
>=
Im Gegensatz zu anderen Produktionen in der Syntax keine Zeichen Zwischen den Token von Rechtsverschiebungs- und Rechtsverschiebungs-Zuweisungsproduktionen sind beliebige Arten (gerade Leerzeichen) zulässig.
Die folgende Produktion wird mithilfe der Rechtsschicht oder der Rechtsschichtzuweisung geändert.
Shift-Ausdruck: (Shift-Ausdruck:)
Additiv-Ausdruck (zusätzlicher Ausdruck)
Shift-Ausdruck 6e4b25b5f32f3be430a1426123614b4e
ddd0620b42f67e0c3fae74991e3d1d10=
<=
(Ende der Generika)
******* ***********************