10 Konvertierungen
10.1 Allgemein
Eine Konvertierung bewirkt, dass ein Ausdruck in einen bestimmten Typ konvertiert oder behandelt wird. In dem früheren Fall kann eine Konvertierung eine Änderung der Darstellung beinhalten. Konvertierungen können implizit oder explizit sein, und dies bestimmt, ob eine explizite Umwandlung erforderlich ist.
Beispiel: Die Konvertierung von Typ
int
zu Typlong
ist implizit, sodass Ausdrücke des Typsint
implizit als Typlong
behandelt werden können. Die entgegengesetzte Konvertierung von Typlong
zu Typint
ist explizit und daher ist eine explizite Umwandlung erforderlich.int a = 123; long b = a; // implicit conversion from int to long int c = (int) b; // explicit conversion from long to int
Endbeispiel
Einige Konvertierungen werden von der Sprache definiert. Programme können auch eigene Konvertierungen (§10.5) definieren.
Einige Konvertierungen in der Sprache werden von Ausdrücken in Typen definiert, andere von Typen zu Typen. Eine Konvertierung aus einem Typ gilt für alle Ausdrücke mit diesem Typ.
Beispiel:
enum Color { Red, Blue, Green } // The expression 0 converts implicitly to enum types Color c0 = 0; // Other int expressions need explicit conversion Color c1 = (Color)1; // Conversion from null expression (no type) to string string x = null; // Conversion from lambda expression to delegate type Func<int, int> square = x => x * x;
Endbeispiel
10.2 Implizite Konvertierungen
10.2.1 Allgemein
Die folgenden Konvertierungen werden als implizite Konvertierungen klassifiziert:
- Identitätskonvertierungen (§10.2.2)
- Implizite numerische Konvertierungen (§10.2.3)
- Implizite Enumerationskonvertierungen (§10.2.4)
- Implizite interpolierte Zeichenfolgenkonvertierungen (§10.2.5)
- Implizite Verweiskonvertierungen (§10.2.8)
- Boxumwandlungen (§10.2.9)
- Implizite dynamische Konvertierungen (§10.2.10)
- Konvertierungen impliziter Typparameter (§10.2.12)
- Implizite Konstantenausdruckkonvertierungen (§10.2.11)
- Benutzerdefinierte (einschließlich aufgehobener) impliziter Konvertierungen (§10.2.14)
- Konvertierungen anonymer Funktionen (§10.2.15)
- Methodengruppenkonvertierungen (§10.2.15)
- Nullliteralumwandlungen (§10.2.7)
- Implizite nullable Konvertierungen (§10.2.6)
- Implizite Tupelkonvertierungen (§10.2.13)
- Standardliteralkonvertierungen (§10.2.16)
- Implizite Wurfkonvertierungen (§10.2.17)
Implizite Konvertierungen können in einer Vielzahl von Situationen auftreten, z. B. Funktionsmemembeaufrufe (§12.6.6), Umwandlungsausdrücke (§12.9.7) und Zuordnungen (§12.21).
Die vordefinierten impliziten Konvertierungen sind immer erfolgreich und führen nie dazu, dass Ausnahmen ausgelöst werden.
Hinweis: Ordnungsgemäß gestaltete, benutzerdefinierte implizite Konvertierungen sollten auch diese Merkmale aufweisen. Endnote
Für die Zwecke der Umwandlung sind die Typen object
und dynamic
identitätskonvertierbar (§10.2.2).
Dynamische Konvertierungen (§10.2.10) gelten jedoch nur für Ausdrücke vom Typ dynamic
(§8.2.4).
10.2.2 Identitätskonvertierung
Eine Identitätskonvertierung konvertiert von jedem Typ in denselben Typ oder einen Typ, der zur Laufzeit gleichwertig ist. Ein Grund für diese Konvertierung ist, dass ein Typ T
oder ein Ausdruck des Typs T
als Konvertierbar für T
sich selbst bezeichnet werden kann. Die folgenden Identitätskonvertierungen sind vorhanden:
- Zwischen
T
undT
, für jeden TypT
. - Zwischen
T
undT?
für jeden ReferenztypT
. - Zwischen
object
unddynamic
. - Zwischen allen Tupeltypen mit derselben Arität und dem entsprechenden konstruierten
ValueTuple<...>
Typ, wenn zwischen jedem Paar entsprechender Elementtypen eine Identitätskonvertierung vorhanden ist. - Zwischen Typen, die aus demselben generischen Typ erstellt wurden, bei dem es eine Identitätskonvertierung zwischen jedem entsprechenden Typargument gibt.
Beispiel: Im Folgenden wird die rekursive Natur der dritten Regel veranschaulicht:
(int a , string b) t1 = (1, "two"); (int c, string d) t2 = (3, "four"); // Identity conversions exist between // the types of t1, t2, and t3. var t3 = (5, "six"); t3 = t2; t2 = t1; var t4 = (t1, 7); var t5 = (t2, 8); // Identity conversions exist between // the types of t4, t5, and t6. var t6 =((8, "eight"), 9); t6 = t5; t5 = t4;
Die Arten von Tupeln
t1
t2
undt3
alle haben zwei Elemente: eineint
gefolgt von einerstring
. Tupel-Elementtypen können sich selbst durch Tupel, wie int4
,t5
, undt6
. Eine Identitätskonvertierung besteht zwischen jedem Paar der entsprechenden Elementtypen, einschließlich geschachtelter Tupel, daher ist eine Identitätskonvertierung zwischen den Typen von Tupelnt4
,t5
undt6
.Endbeispiel
Alle Identitätskonvertierungen sind symmetrisch. Wenn eine Identitätskonvertierung von T₁
zu T₂
" vorhanden ist , dann ist eine Identitätskonvertierung von T₂
zu T₁
" vorhanden. Zwei Typen sind identitätskonvertierbar, wenn eine Identitätskonvertierung zwischen zwei Typen vorhanden ist.
In den meisten Fällen hat eine Identitätskonvertierung zur Laufzeit keine Auswirkung. Da Gleitkommavorgänge jedoch mit höherer Genauigkeit als durch ihren Typ (§8.3.7) durchgeführt werden können, kann die Zuweisung ihrer Ergebnisse zu einem Genauigkeitsverlust führen, und explizite Gusse werden garantiert, um die Genauigkeit auf die vom Typ vorgeschriebenen Genauigkeit zu reduzieren (§12.9.7).
10.2.3 Implizite numerische Konvertierungen
Die impliziten numerischen Konvertierungen sind:
- Von
sbyte
bisshort
,int
,long
, ,float
, ,double
oderdecimal
. - Von
byte
bisshort
,ushort
,int
,uint
,long
, ,ulong
, ,float
oderdecimal
double
. - Von
short
bisint
,long
,float
, ,double
oderdecimal
. - Von
ushort
bisint
,uint
,long
,ulong
, ,float
, , ,double
oderdecimal
. - Von
int
bislong
,float
, ,double
oderdecimal
. - Von
uint
bislong
,ulong
,float
, ,double
oderdecimal
. - Von
long
bisfloat
,double
, oderdecimal
. - Von
ulong
bisfloat
,double
, oderdecimal
. - Von
char
bisushort
,int
,uint
,long
, ,ulong
,float
, , oderdouble
decimal
. - Von
float
indouble
.
Konvertierungen von int
, uint
oder long
in float
und von oder ulong
aus ulong
long
double
können zu einem Verlust der Genauigkeit führen, aber niemals zu einem Verlust der Größe führen. Die anderen impliziten numerischen Konvertierungen verlieren nie Informationen.
Es gibt keine vordefinierten impliziten Konvertierungen in den char
Typ, sodass Werte der anderen integralen Typen nicht automatisch in den char
Typ konvertiert werden.
10.2.4 Implizite Enumerationskonvertierungen
Eine implizite Enumerationskonvertierung ermöglicht eine constant_expression (§12.23) mit einem beliebigen ganzzahligen Typ und dem Wert Null, der in eine beliebige enum_type und in alle nullable_value_type konvertiert werden kann, deren zugrunde liegender Typ ein enum_type ist. Im letzteren Fall wird die Konvertierung ausgewertet, indem sie in die zugrunde liegende enum_type konvertiert und das Ergebnis umschlossen (§8.3.12).
10.2.5 Implizite interpolierte Zeichenfolgenkonvertierungen
Durch eine implizite interpolierte Zeichenfolgenkonvertierung kann eine interpolated_string_expression (§12.8.3) in oder System.FormattableString
(die implementiert wirdSystem.IFormattable
) konvertiert System.IFormattable
werden.
Wenn diese Konvertierung angewendet wird, wird kein Zeichenfolgenwert aus der interpolierten Zeichenfolge zusammengesetzt. Stattdessen wird eine Instanz System.FormattableString
erstellt, wie weiter in §12.8.3 beschrieben.
10.2.6 Implizite nullfähige Konvertierungen
Die impliziten nullablen Konvertierungen sind diese nullfähigen Konvertierungen (§10.6.1), die von impliziten vordefinierten Konvertierungen abgeleitet sind.
10.2.7 Nullliteralumwandlungen
Es ist eine implizite Konvertierung aus dem null
Literal in einen beliebigen Bezugstyp oder nullablen Werttyp vorhanden. Diese Konvertierung erzeugt einen Nullverweis, wenn der Zieltyp ein Bezugstyp oder der Nullwert (§8.3.12) des angegebenen Nullwerttyps ist.
10.2.8 Implizite Referenzkonvertierungen
Die impliziten Verweiskonvertierungen sind:
- Von jedem reference_type bis zu
object
unddynamic
. - Von jeder class_type bis zu jeder class_type
S
T
, bereitgestelltS
wird abgeleitet von .T
- Von jedem class_type bis zu jeder interface_type
S
T
, bereitgestellt wird.S
T
- Von jedem interface_type bis zu einem beliebigen interface_type
S
T
, bereitgestelltS
wird von .T
- Von einem array_type
S
mit einem ElementtypSᵢ
bis zu einem array_typeT
mit einem ElementtypTᵢ
, vorausgesetzt, alle folgenden Sind erfüllt:S
undT
unterscheiden sich nur im Elementtyp. Mit anderen Worten,S
undT
sie haben dieselbe Anzahl von Dimensionen.- Eine implizite Verweiskonvertierung ist von
Sᵢ
zuTᵢ
.
- Von einem eindimensionalen Arraytyp
S[]
inSystem.Collections.Generic.IList<T>
System.Collections.Generic.IReadOnlyList<T>
, und deren Basisschnittstellen, vorausgesetzt, es gibt eine implizite Identität oder Referenzkonvertierung vonS
zuT
. - Von jedem array_type bis zu
System.Array
den von ihr implementierten Schnittstellen. - Von jedem delegate_type bis zu
System.Delegate
den von ihr implementierten Schnittstellen. - Vom Nullliteral (§6.4.5.7) bis hin zu einem beliebigen Bezugstyp.
- Von jeder reference_type in eine reference_type
T
, wenn sie über eine implizite Identität oder Einen Verweiskonvertierung in eine reference_typeT₀
verfügt undT₀
eine Identitätskonvertierung inT
. - Von jedem reference_type in eine Schnittstelle oder einen Delegattyp
T
, wenn er über eine implizite Identitäts- oder Verweiskonvertierung in eine Schnittstelle oder einen DelegattypT₀
verfügt undT₀
varianzkonvertierbar (§18.2.3.3) inT
. - Implizite Konvertierungen mit Typparametern, die als Referenztypen bekannt sind. Weitere Informationen zu impliziten Konvertierungen mit Typparametern finden Sie unter §10.2.12 .
Die impliziten Verweiskonvertierungen sind die Konvertierungen zwischen reference_type, die nachweislich immer erfolgreich sind und daher zur Laufzeit keine Überprüfungen erfordern.
Verweiskonvertierungen, implizit oder explizit, ändern niemals die referenzielle Identität des zu konvertierenden Objekts.
Hinweis: Während eine Verweiskonvertierung den Typ des Verweises ändern kann, ändert sie niemals den Typ oder den Wert des Objekts, auf das verwiesen wird. Endnote
10.2.9 Boxing Konvertierungen
Bei einer Boxumwandlung kann ein value_type implizit in eine reference_type konvertiert werden. Die folgenden Boxumwandlungen sind vorhanden:
- Von einem beliebigen value_type in den Typ
object
. - Von einem beliebigen value_type in den Typ
System.ValueType
. - Von einem beliebigen enum_type in den Typ
System.Enum
. - Von jedem non_nullable_value_type bis zu allen interface_type, die vom non_nullable_value_type implementiert werden.
- Von jedem non_nullable_value_type auf jede interface_type
I
, sodass es eine Boxumwandlung von der non_nullable_value_type in eine andere interface_typeI₀
gibt undI₀
eine Identitätskonvertierung aufweistI
. - Von jedem non_nullable_value_type auf jede interface_type
I
, sodass es eine Boxumwandlung von der non_nullable_value_type in eine andere interface_typeI₀
gibt undI₀
varianzkonvertierbar (§18.2.3.3) inI
. - Von jedem nullable_value_type auf jede reference_type, in der eine Boxumwandlung vom zugrunde liegenden Typ des nullable_value_type in die reference_type vorhanden ist.
- Von einem Typparameter, der nicht als Verweistyp auf einen beliebigen Typ bekannt ist, sodass die Konvertierung durch §10.2.12 zulässig ist.
Das Boxen eines Werts eines Nicht-Null-Wert-Typs besteht darin, eine Objektinstanz zu zuordnen und den Wert in diese Instanz zu kopieren.
Beim Boxen eines Werts einer nullable_value_type wird ein Nullverweis erzeugt, wenn es sich um den Nullwert handelt (HasValue
ist falsch) oder das Ergebnis der Entwrappung und des Boxens des zugrunde liegenden Werts andernfalls.
Hinweis: Der Prozess des Boxens kann sich in Bezug auf das Vorhandensein einer Boxklasse für jeden Werttyp vorstellen. Erwägen Sie beispielsweise eine Implementierung einer
struct S
SchnittstelleI
mit einer boxenden Klasse, die aufgerufen wirdS_Boxing
.interface I { void M(); } struct S : I { public void M() { ... } } sealed class S_Boxing : I { S value; public S_Boxing(S value) { this.value = value; } public void M() { value.M(); } }
Das Boxen eines Typs
v
S
besteht nun darin, den Ausdrucknew S_Boxing(v)
auszuführen und die resultierende Instanz als Wert des Zieltyps der Konvertierung zurückzugeben. So werden die AussagenS s = new S(); object box = s;
kann man sich wie die folgenden gedanken machen:
S s = new S(); object box = new S_Boxing(s);
Der oben beschriebene Boxentyp ist nicht wirklich vorhanden. Stattdessen weist ein Boxwert des Typs
S
den LaufzeittypS
auf, und ein Laufzeittyp überprüft denis
Operator mit einem Werttyp, da der rechte Operand testet, ob der linke Operand eine boxte Version des rechten Operanden ist. Beispiel:int i = 123; object box = i; if (box is int) { Console.Write("Box contains an int"); }
gibt Folgendes aus:
Box contains an int
Eine Boxumwandlung bedeutet, dass eine Kopie des Schachtelwerts vorgenommen wird. Dies unterscheidet sich von einer Konvertierung einer reference_type in Typ
object
, in der der Wert weiterhin auf dieselbe Instanz verweist und einfach als der weniger abgeleitete Typobject
betrachtet wird. Beispiel:struct Point { public int x, y; public Point(int x, int y) { this.x = x; this.y = y; } } class A { void M() { Point p = new Point(10, 10); object box = p; p.x = 20; Console.Write(((Point)box).x); } }
gibt den Wert 10 auf der Konsole aus, da der implizite Boxvorgang, der in der Zuweisung
p
auftritt,box
bewirkt, dass der Wertp
kopiert wird. StattdessenPoint
wurde derclass
Wert 20 ausgegeben, dap
erbox
auf dieselbe Instanz verweist.Die Analogie einer Boxklasse sollte nicht mehr als ein hilfreiches Werkzeug zur Darstellung der Funktionsweise des Boxens verwendet werden. Es gibt zahlreiche subtile Unterschiede zwischen dem von dieser Spezifikation beschriebenen Verhalten und dem Verhalten, das dazu führen würde, dass Boxen genau auf diese Weise implementiert werden.
Endnote
10.2.10 Implizite dynamische Konvertierungen
Eine implizite dynamische Konvertierung ist aus einem Ausdruck des Typs "dynamisch" in einen beliebigen Typ T
vorhanden. Die Konvertierung ist dynamisch gebunden §12.3.3, was bedeutet, dass eine implizite Konvertierung zur Laufzeit vom Laufzeittyp des Ausdrucks in .T
Wenn keine Konvertierung gefunden wird, wird eine Laufzeit ausnahme ausgelöst.
Diese implizite Konvertierung verstößt scheinbar gegen den Hinweis am Anfang von §10.2 , dass eine implizite Konvertierung niemals eine Ausnahme verursachen sollte. Es ist jedoch nicht die Konvertierung selbst, sondern die Suche nach der Konvertierung, die die Ausnahme verursacht. Das Risiko von Laufzeitausnahmen ist inhärent bei der Verwendung der dynamischen Bindung. Wenn die dynamische Bindung der Konvertierung nicht gewünscht wird, kann der Ausdruck zuerst in den gewünschten Typ konvertiert object
werden.
Beispiel: Im Folgenden werden implizite dynamische Konvertierungen veranschaulicht:
object o = "object"; dynamic d = "dynamic"; string s1 = o; // Fails at compile-time – no conversion exists string s2 = d; // Compiles and succeeds at run-time int i = d; // Compiles but fails at run-time – no conversion exists
Die Zuordnungen und
s2
i
beide verwenden implizite dynamische Konvertierungen, bei denen die Bindung der Vorgänge bis zur Laufzeit angehalten wird. Zur Laufzeit werden implizite Konvertierungen vom Laufzeittyp (d
string
) in den Zieltyp gesucht. Eine Konvertierung wird gefundenstring
, aber nicht inint
.Endbeispiel
10.2.11 Konvertierung impliziter Konstantenausdruck
Eine implizite Konstantenausdruckkonvertierung ermöglicht die folgenden Konvertierungen:
- Ein constant_expression (§12.23) des Typs kann in Typ
int
sbyte
,byte
, ,short
ushort
, oderuint
,ulong
vorausgesetzt, der Wert der constant_expression innerhalb des Bereichs des Zieltyps konvertiert werden. - Ein constant_expression des Typs
long
kann in Typulong
konvertiert werden, vorausgesetzt, der Wert der constant_expression ist nicht negativ.
10.2.12 Implizite Konvertierungen mit Typparametern
Für eine type_parameterT
, die als Bezugstyp (§15.2.5) bekannt ist, sind die folgenden impliziten Referenzkonvertierungen (§10.2.8) vorhanden:
- Von
T
der effektiven BasisklasseC
, vonT
bis zu jeder Basisklasse vonC
, und vonT
bis zu jeder Schnittstelle, die vonC
. - Von
T
bis zu einem interface_typeI
imT
effektiven Schnittstellensatz und vonT
einer beliebigen Basisschnittstelle.I
- Von
T
einem TypparameterU
, der vonT
(§15.2.5) abhängtU
.Hinweis: Da
T
bekannt ist, dass es sich um einen Verweistyp handelt, ist derT
LaufzeittypU
immer ein Verweistyp, auch wennU
zur Kompilierungszeit kein Verweistyp bekannt ist. Endnote - Von der Nullliteral (§6.4.5.7) bis T.
Bei einer type_parameterT
, die nicht als Bezugstyp §15.2.5 bekannt ist, gelten die folgenden Konvertierungen T
als Boxumwandlungen (§10.2.9) zur Kompilierungszeit. Wenn es sich um einen Werttyp handelt, wird die Konvertierung zur Laufzeit T
als Boxumwandlung ausgeführt. Wenn es sich um einen Verweistyp handelt, wird die Konvertierung zur Laufzeit T
als implizite Referenzkonvertierung oder Identitätskonvertierung ausgeführt.
- Von
T
der effektiven BasisklasseC
, vonT
bis zu jeder Basisklasse vonC
, und vonT
bis zu jeder Schnittstelle, die vonC
.Hinweis:
C
Ist einer der TypenSystem.Object
,System.ValueType
oderSystem.Enum
(andernfallsT
wäre bekannt als Bezugstyp). Endnote - Von
T
bis zu einem interface_typeI
imT
effektiven Schnittstellensatz und vonT
einer beliebigen Basisschnittstelle.I
Für eine type_parameterT
, die nicht als Verweistyp bekannt ist, ist eine implizite Konvertierung von T
einem Typparameter U
abhängigT
.U
Wenn es sich bei der Laufzeit T
um einen Werttyp handelt und U
ein Bezugstyp ist, wird die Konvertierung als Boxumwandlung ausgeführt. Zur Laufzeit, wenn beide T
Typen sind und U
Werttypen sind, T
und U
sie sind notwendigerweise derselbe Typ, und es wird keine Konvertierung ausgeführt. Wenn es sich bei der Laufzeit T
um einen Verweistyp handelt, ist dies U
notwendigerweise auch ein Verweistyp, und die Konvertierung wird als implizite Referenzkonvertierung oder Identitätskonvertierung (§15.2.5) ausgeführt.
Für einen bestimmten Typparameter T
sind die folgenden weiteren impliziten Konvertierungen vorhanden:
- Von
T
zu einem VerweistypS
, wenn er über eine implizite Konvertierung in einen VerweistypS₀
verfügt undS₀
eine Identitätskonvertierung inS
. Zur Laufzeit wird die Konvertierung auf die gleiche Weise ausgeführt wie die Konvertierung inS₀
. - Von
T
einem SchnittstellentypI
, wenn er über eine implizite Konvertierung in einen SchnittstellentypI₀
verfügt undI₀
varianzkonvertierbar inI
(§18.2.3.3) ist. Wenn es sich um einen Werttyp handelt, wird die Konvertierung zur LaufzeitT
als Boxumwandlung ausgeführt. Andernfalls wird die Konvertierung als implizite Verweiskonvertierung oder Identitätskonvertierung ausgeführt.
In allen Fällen stellen die Regeln sicher, dass eine Konvertierung als Boxkonvertierung ausgeführt wird, wenn und nur, wenn die Konvertierung zur Laufzeit von einem Werttyp in einen Verweistyp erfolgt.
10.2.13 Implizite Tupelkonvertierungen
Eine implizite Konvertierung besteht aus einem Tupelausdruck E
in einen Tupeltyp T
, wenn E
die gleiche Arität wie T
und eine implizite Konvertierung von jedem Element in E
den entsprechenden Elementtyp T
vorhanden ist. Die Konvertierung erfolgt durch Erstellen einer Instanz des T
entsprechenden System.ValueTuple<...>
Typs und Initialisieren der einzelnen Felder in der Reihenfolge von links nach rechts durch Auswerten des entsprechenden Tupelelementausdrucks E
, Konvertieren in den entsprechenden Elementtyp der T
verwendung der gefundenen impliziten Konvertierung und Initialisieren des Felds mit dem Ergebnis.
Wenn ein Elementname im Tupelausdruck nicht mit einem entsprechenden Elementnamen im Tupeltyp übereinstimmt, wird eine Warnung ausgegeben.
Beispiel:
(int, string) t1 = (1, "One"); (byte, string) t2 = (2, null); (int, string) t3 = (null, null); // Error: No conversion (int i, string s) t4 = (i: 4, "Four"); (int i, string) t5 = (x: 5, s: "Five"); // Warning: Names are ignored
Die Deklarationen von
t1
,t2
undt4
t5
alle gültig, da implizite Konvertierungen aus den Elementausdrücken in die entsprechenden Elementtypen vorhanden sind. Die Deklaration vont3
ist ungültig, da keine Konvertierung vonnull
zuint
. Die Deklaration führtt5
zu einer Warnung, da sich die Elementnamen im Tupelausdruck von denen im Tupeltyp unterscheiden.Endbeispiel
10.2.14 Benutzerdefinierte implizite Konvertierungen
Eine benutzerdefinierte implizite Konvertierung besteht aus einer optionalen impliziten Standardkonvertierung, gefolgt von der Ausführung eines benutzerdefinierten impliziten Konvertierungsoperators, gefolgt von einer anderen optionalen impliziten Standardkonvertierung. Die genauen Regeln für die Auswertung benutzerdefinierter impliziter Konvertierungen werden in §10.5.4 beschrieben.
10.2.15 Konvertierungen anonymer Funktionen und Methodengruppenkonvertierungen
Anonyme Funktionen und Methodengruppen haben keine Typen in und von sich selbst, aber sie können implizit in Stellvertretungstypen konvertiert werden. Darüber hinaus können einige Lambda-Ausdrücke implizit in Ausdrucksstrukturtypen konvertiert werden. Anonyme Funktionskonvertierungen werden in §10.7 und Methodengruppenkonvertierungen in §10.8 ausführlicher beschrieben.
10.2.16 Standardliteralkonvertierungen
Eine implizite Konvertierung besteht aus einem default_literal (§12.8.21) in einen beliebigen Typ. Diese Konvertierung erzeugt den Standardwert (§9.3) des abgeleiteten Typs.
10.2.17 Implizite Auslösen von Konvertierungen
Während Der Auslösen von Ausdrücken keinen Typ aufweist, werden sie möglicherweise implizit in einen beliebigen Typ konvertiert.
10.3 Explizite Konvertierungen
10.3.1 Allgemein
Die folgenden Konvertierungen werden als explizite Konvertierungen klassifiziert:
- Alle impliziten Konvertierungen (§10.2)
- Explizite numerische Konvertierungen (§10.3.2)
- Explizite Enumerationskonvertierungen (§10.3.3)
- Explizite nullable Konvertierungen (§10.3.4)
- Explizite Tupelkonvertierungen (§10.3.6)
- Explizite Verweiskonvertierungen (§10.3.5)
- Explizite Schnittstellenkonvertierungen
- Unboxing-Konvertierungen (§10.3.7)
- Explizite Typparameterkonvertierungen (§10.3.8)
- Benutzerdefinierte explizite Konvertierungen (§10.3.9)
Explizite Konvertierungen können in Umwandlungsausdrücken (§12.9.7) auftreten.
Die Gruppe expliziter Konvertierungen enthält alle impliziten Konvertierungen.
Hinweis: Dies ermöglicht beispielsweise die Verwendung einer expliziten Umwandlung, wenn eine implizite Identitätskonvertierung vorhanden ist, um die Auswahl einer bestimmten Methodenüberladung zu erzwingen. Endnote
Die expliziten Konvertierungen, die nicht implizite Konvertierungen sind Konvertierungen, die nicht immer erfolgreich sein können, Konvertierungen, die bekannt sind, dass Informationen verloren gehen, und Konvertierungen in allen Domänen von Typen, die ausreichend unterschiedlich sind, um eine explizite Notation zu erzielen.
10.3.2 Explizite numerische Konvertierungen
Die expliziten numerischen Konvertierungen sind die Konvertierungen von einer numeric_type in eine andere numeric_type , für die eine implizite numerische Konvertierung (§10.2.3) noch nicht vorhanden ist:
- Von
sbyte
bisbyte
,ushort
,uint
, ,ulong
oderchar
. - Von
byte
bissbyte
oderchar
. - Von
short
bissbyte
,byte
,ushort
, ,uint
, ,ulong
oderchar
. - Von
ushort
bissbyte
,byte
, ,short
oderchar
. - Von
int
bissbyte
,byte
,short
,ushort
, ,uint
, , ,ulong
oderchar
. - Von
uint
bissbyte
,byte
,short
, ,ushort
, ,int
oderchar
. - Von
long
bissbyte
,byte
,short
,ushort
, ,int
,uint
, , oderulong
char
. - Von
ulong
bissbyte
,byte
,short
,ushort
, ,int
,uint
, , oderlong
char
. - Von
char
bissbyte
,byte
, odershort
. - Von
float
zusbyte
,byte
,short
,ushort
,int
, ,uint
, ,long
,ulong
, , oderchar
decimal
. - Von
double
zusbyte
,byte
,short
,ushort
,int
, ,uint
,long
,char
ulong
, , , oderfloat
decimal
. - Von
decimal
zusbyte
,byte
,short
,ushort
,int
, ,uint
,long
,char
ulong
, , , oderfloat
double
.
Da die expliziten Konvertierungen alle impliziten und expliziten numerischen Konvertierungen enthalten, ist es immer möglich, von jedem beliebigen numeric_type in eine andere numeric_type mithilfe eines Umwandlungsausdrucks (§12.9.7) zu konvertieren.
Die expliziten numerischen Konvertierungen verlieren möglicherweise Informationen oder führen möglicherweise dazu, dass Ausnahmen ausgelöst werden. Eine explizite numerische Konvertierung wird wie folgt verarbeitet:
- Für eine Konvertierung von einem integralen Typ in einen anderen integralen Typ hängt die Verarbeitung vom Überlaufüberprüfungskontext (§12.8.20) ab, in dem die Konvertierung stattfindet:
- In einem
checked
Kontext ist die Konvertierung erfolgreich, wenn sich der Wert des Quellopernden im Bereich des Zieltyps befindet, löst jedoch einenSystem.OverflowException
Auslöser aus, wenn sich der Wert des Quellopernden außerhalb des Zieltyps befindet. - In einem
unchecked
Kontext ist die Konvertierung immer erfolgreich und wird wie folgt fortgesetzt.- Wenn der Quelltyp größer als der Zieltyp ist, wird der Quellwert abgeschnitten, indem die wichtigsten "zusätzlichen" Bits verworfen werden. Das Ergebnis wird dann als Wert des Zieltyps behandelt.
- Wenn der Quelltyp die gleiche Größe wie der Zieltyp aufweist, wird der Quellwert als Wert des Zieltyps behandelt.
- In einem
- Bei einer Umwandlung von
decimal
in einen integralen Typ wird der Quellwert auf null auf den nächsten integralen Wert gerundet, und dieser integrale Wert wird zum Ergebnis der Konvertierung. Wenn sich der resultierende integrale Wert außerhalb des Bereichs des Zieltyps befindet, wird einSystem.OverflowException
Fehler ausgelöst. - Bei einer Konvertierung von
float
oderdouble
zu einem integralen Typ hängt die Verarbeitung vom Überlaufüberprüfungskontext (§12.8.20) ab, in dem die Konvertierung stattfindet:- In einem überprüften Kontext wird die Konvertierung wie folgt fortgesetzt:
- Wenn der Wert des Operanden naN oder unendlich ist, wird ein
System.OverflowException
Wurf ausgelöst. - Andernfalls wird der Quellopernd auf den nächsten integralen Wert gerundet. Wenn sich dieser integrale Wert innerhalb des Bereichs des Zieltyps befindet, ist dieser Wert das Ergebnis der Konvertierung.
- Andernfalls wird eine
System.OverflowException
ausgelöst.
- Wenn der Wert des Operanden naN oder unendlich ist, wird ein
- In einem deaktivierten Kontext ist die Konvertierung immer erfolgreich und wird wie folgt fortgesetzt.
- Wenn der Wert des Operanden NaN oder unendlich ist, ist das Ergebnis der Konvertierung ein nicht angegebener Wert des Zieltyps.
- Andernfalls wird der Quellopernd auf den nächsten integralen Wert gerundet. Wenn sich dieser integrale Wert innerhalb des Bereichs des Zieltyps befindet, ist dieser Wert das Ergebnis der Konvertierung.
- Andernfalls ist das Ergebnis der Konvertierung ein nicht angegebener Wert des Zieltyps.
- In einem überprüften Kontext wird die Konvertierung wie folgt fortgesetzt:
- Bei einer Konvertierung von
double
in " infloat
" wird derdouble
Wert auf den nächstenfloat
Wert gerundet. Wenn derdouble
Wert zu klein ist, um als einfloat
Wert darzustellen, wird das Ergebnis null mit demselben Vorzeichen wie der Wert. Wenn die Größe desdouble
Werts zu groß ist, um als einfloat
Wert darzustellen, wird das Ergebnis unendlich mit demselben Zeichen wie der Wert. Wenn derdouble
Wert "NaN" lautet, lautet das Ergebnis auch "NaN". - Bei einer Konvertierung von
float
oderdouble
in " indecimal
" wird der Quellwert in Darstellung konvertiert und bei Bedarf auf die nächste Zahl gerundetdecimal
(§8.3.8).- Wenn der Quellwert zu klein ist, um als ein
decimal
Objekt darzustellen, wird das Ergebnis null, wobei das Vorzeichen des ursprünglichen Werts beibehalten wird, wenndecimal
signierte Nullwerte unterstützt werden. - Wenn die Größe des Quellwerts zu groß ist, um ihn als
decimal
Unendlichkeit darzustellen, bleibt das Ergebnis unendlich, wenn die Dezimaldarstellung Infinitäten unterstützt, andernfalls wird eine System.OverflowException ausgelöst. - Wenn der Quellwert "NaN" lautet, lautet das Ergebnis "NaN", wenn die Dezimaldarstellung NaNs unterstützt. andernfalls wird eine System.OverflowException ausgelöst.
- Wenn der Quellwert zu klein ist, um als ein
- Bei einer Konvertierung von
decimal
zufloat
oderdouble
, wird derdecimal
Wert auf den nächstendouble
oderfloat
Wert gerundet. Wenn die Größe des Quellwerts zu groß ist, um den Zieltyp darzustellen, oder dieser Wert unendlich ist, bleibt das Ergebnis unendlich, um das Vorzeichen des ursprünglichen Werts beizubehalten. Wenn der Quellwert "NaN" lautet, lautet das Ergebnis "NaN". Diese Konvertierung verliert zwar möglicherweise die Genauigkeit, führt jedoch nie dazu, dass eine Ausnahme ausgelöst wird.
Hinweis: Der
decimal
Typ ist nicht erforderlich, um Infinitäten oder NaN-Werte zu unterstützen, kann dies jedoch tun. Sein Bereich kann kleiner als der Bereichfloat
unddouble
sein, ist jedoch nicht garantiert. Fürdecimal
Darstellungen ohne Infinitäten oder NaN-Werte und mit einem Bereich kleiner alsfloat
, wird das Ergebnis einer Konvertierung vondecimal
entweder in oderfloat
nie unendlich oderdouble
NaN sein. Endnote
10.3.3 Explizite Enumerationskonvertierungen
Die expliziten Enumerationskonvertierungen sind:
- Von
sbyte
, ,byte
,short
,ushort
,uint
int
,long
,ulong
,char
,float
, , ,double
oderdecimal
zu einem beliebigen enum_type. - Von jeder enum_type in
sbyte
,byte
,short
,int
ushort
, ,uint
, ,long
,ulong
,char
,float
, , , , oderdouble
decimal
. - Von jedem enum_type bis zu allen anderen enum_type.
Eine explizite Enumerationskonvertierung zwischen zwei Typen wird verarbeitet, indem alle teilnehmenden enum_type als zugrunde liegender Typ dieses enum_type behandelt werden und dann eine implizite oder explizite numerische Konvertierung zwischen den resultierenden Typen ausgeführt wird.
Beispiel: Bei einem enum_type
E
mit und dem zugrunde liegenden Typ vonint
, wird eine Konvertierung vonE
inbyte
eine explizite numerische Konvertierung (§10.3.2) vonint
inbyte
, und eine Konvertierung vonbyte
zuE
" wird als implizite numerische Konvertierung (§10.2.3) vonbyte
zuint
verarbeitet . Endbeispiel
10.3.4 Explizite nullfähige Konvertierungen
Die expliziten nullablen Konvertierungen sind diese nullfähigen Konvertierungen (§10.6.1) abgeleitet von expliziten und impliziten vordefinierten Konvertierungen.
10.3.5 Explizite Verweiskonvertierungen
Die expliziten Verweiskonvertierungen sind:
- Vom Objekt bis zu allen anderen reference_type.
- Von jedem class_type bis zu jeder class_type
S
T
, bereitgestelltS
ist eine Basisklasse von .T
- Von jedem class_type
S
bis zu einer interface_typeT
, vorausgesetztS
, nicht versiegelt und bereitgestelltS
wird nicht implementiertT
. - Von jedem interface_type bis zu jeder class_type
T
S
, sofern nichtT
versiegelt oder bereitgestelltT
wird.S
- Von jedem interface_type
S
bis zu einer interface_typeT
, vorausgesetztS
, wird nicht vonT
. - Von einem array_type
S
mit einem ElementtypSᵢ
bis zu einem array_typeT
mit einem ElementtypTᵢ
, vorausgesetzt, alle folgenden Sind erfüllt:S
undT
unterscheiden sich nur im Elementtyp. Mit anderen Worten,S
undT
sie haben dieselbe Anzahl von Dimensionen.- Eine explizite Verweiskonvertierung ist von
Sᵢ
zuTᵢ
.
- Von
System.Array
und den von ihr implementierten Schnittstellen bis zu jedem array_type. - Von einer eindimensionalen array_type
S[]
inSystem.Collections.Generic.IList<T>
System.Collections.Generic.IReadOnlyList<T>
, und deren Basisschnittstellen, vorausgesetzt, es gibt eine Identitätskonvertierung oder explizite Verweiskonvertierung vonS
zuT
. - Von
System.Collections.Generic.IList<S>
,System.Collections.Generic.IReadOnlyList<S>
und deren Basisschnittstellen zu einem eindimensionalen ArraytypT[]
, vorausgesetzt, es gibt eine Identitätskonvertierung oder explizite Verweiskonvertierung vonS
zu T. - Von
System.Delegate
und den Schnittstellen, die sie für alle delegate_type implementiert. - Von einem Verweistyp
S
zu einem VerweistypT
, wenn er über eine explizite Verweiskonvertierung vonS
einem Verweistyp in einen VerweistypT₀
verfügt undT₀
eine Identitätskonvertierung vonT₀
zuT
. - Von einem Verweistyp
S
auf eine Schnittstelle oder einen DelegattypT
, wenn es eine explizite Verweiskonvertierung vonS
einer Schnittstelle oder einem DelegattypT₀
gibt und entwederT₀
varianzkonvertierbar in §18.2.3.3 ist oderT
varianzkonvertierbarT
T₀
ist. - Von
D<S₁...Sᵥ>
woD<T₁...Tᵥ>
D<X₁...Xᵥ>
aus ein generischer Delegattyp,D<S₁...Sᵥ>
nicht kompatibel mit oder identischD<T₁...Tᵥ>
mit , und für jeden TypparameterXᵢ
derD
folgenden Haltebereiche:- Wenn
Xᵢ
dies invariant ist, ist diesSᵢ
identisch mitTᵢ
. - Wenn
Xᵢ
kovariant ist, gibt es eine Identitätskonvertierung, implizite Verweiskonvertierung oder explizite Verweiskonvertierung vonSᵢ
inTᵢ
. - Wenn
Xᵢ
dies kontravariant ist,Sᵢ
sind sieTᵢ
entweder identisch oder beide Bezugstypen.
- Wenn
- Explizite Konvertierungen mit Typparametern, die als Referenztypen bekannt sind. Weitere Informationen zu expliziten Konvertierungen mit Typparametern finden Sie unter §10.3.8.
Bei den expliziten Verweiskonvertierungen handelt es sich um konvertierungen zwischen reference_type, die Laufzeitüberprüfungen erfordern, um sicherzustellen, dass sie korrekt sind.
Damit eine explizite Verweiskonvertierung zur Laufzeit erfolgreich ausgeführt werden kann, muss null
der Wert des Quellopernden oder der Typ des Objekts, auf das vom Quellopernden verwiesen wird, ein Typ sein, der durch eine implizite Verweiskonvertierung in den Zieltyp konvertiert werden kann (§10.2.8). Wenn eine explizite Verweiskonvertierung fehlschlägt, wird ein System.InvalidCastException
Fehler ausgelöst.
Hinweis: Verweiskonvertierungen, implizit oder explizit, ändern niemals den Wert des Verweises selbst (§8.2.1), nur dessen Typ; weder den Typ noch den Wert des Objekts, auf das verwiesen wird. Endnote
10.3.6 Explizite Tupelkonvertierungen
Eine explizite Konvertierung besteht aus einem Tupelausdruck E
in einen Tupeltyp T
, wenn E
die gleiche Arität wie T
und eine implizite oder explizite Konvertierung von jedem Element in E
den entsprechenden Elementtyp T
vorhanden ist. Die Konvertierung erfolgt durch Erstellen einer Instanz des T
entsprechenden System.ValueTuple<...>
Typs und Initialisieren der einzelnen Felder in der Reihenfolge von links nach rechts durch Auswerten des entsprechenden Tupelelementausdrucks E
, Konvertieren in den entsprechenden Elementtyp der Verwendung der T
explizit gefundenen Konvertierung und Initialisieren des Felds mit dem Ergebnis.
10.3.7 Unboxing Konvertierungen
Durch eine Unboxing-Konvertierung kann ein reference_type explizit in eine value_type konvertiert werden. Die folgenden Unboxing-Konvertierungen sind vorhanden:
- Vom Typ
object
bis zu einer beliebigen value_type. - Vom Typ
System.ValueType
bis zu einer beliebigen value_type. - Vom Typ
System.Enum
bis zu einem beliebigen enum_type. - Von jedem interface_type bis zu jeder non_nullable_value_type, die die interface_type implementiert.
- Von jeder interface_type zu jeder non_nullable_value_type
I
, in der eine Posteingangskonvertierung von einem interface_type in den non_nullable_value-TypI₀
und eine Identitätskonvertierung vonI
in .I₀
- Von jedem interface_type auf jede non_nullable_value_type
I
, in der eine Unboxing-Konvertierung von einer interface_typeI₀
in die non_nullable_value_type vorhanden ist und entwederI₀
variance_convertible inI
oderI
varianzkonvertierbar istI₀
(§18.2.3.3). - Von jeder reference_type zu jeder nullable_value_type, in der eine Unboxing-Konvertierung von reference_type in die zugrunde liegende non_nullable_value_type der nullable_value_type vorhanden ist.
- Von einem Typparameter, der kein Werttyp zu einem Beliebigen Typ ist, sodass die Konvertierung durch §10.3.8 zulässig ist.
Ein Unboxing-Vorgang für eine non_nullable_value_type besteht darin, zuerst zu überprüfen, ob die Objektinstanz ein Boxwert des angegebenen non_nullable_value_type ist und dann den Wert aus der Instanz kopiert.
Wenn der Posteingang an eine nullable_value_type aufgehoben wird, wird der Nullwert des nullable_value_type erzeugt, wenn der Quellopernd ist null
, oder das umbrochene Ergebnis des Unboxings der Objektinstanz an den zugrunde liegenden Typ der nullable_value_type andernfalls.
Hinweis: Die in §10.2.9 beschriebene Imaginäre Boxklasse besteht aus der Ausführung des Ausdrucks
((S_Boxing)box).value
durch eine Unboxumwandlung eines Objektfelds in ein value_typeS
. So werden die Aussagenobject box = new S(); S s = (S)box;
Konzeptionell entsprechen
object box = new S_Boxing(new S()); S s = ((S_Boxing)box).value;
Endnote
Für eine Unboxing-Konvertierung in eine bestimmte non_nullable_value_type zur Laufzeit erfolgreich sein soll, muss der Wert des Quellopernden ein Bezug auf einen Boxwert dieses non_nullable_value_type sein. Wenn der Quellopernd null
ausgelöst System.NullReferenceException
wird. Wenn der Quellopernd ein Verweis auf ein inkompatibles Objekt ist, wird ein System.InvalidCastException
Fehler ausgelöst.
Damit eine Unboxing-Konvertierung zu einem bestimmten nullable_value_type zur Laufzeit erfolgreich ausgeführt werden kann, muss der Wert des Quellopernden null oder ein Verweis auf einen Feldwert der zugrunde liegenden non_nullable_value_type der nullable_value_type sein. Wenn der Quellopernd ein Verweis auf ein inkompatibles Objekt ist, wird ein System.InvalidCastException
Fehler ausgelöst.
10.3.8 Explizite Konvertierungen mit Typparametern
Für eine type_parameterT
, die als Bezugstyp (§15.2.5) bekannt ist, sind die folgenden expliziten Verweiskonvertierungen (§10.3.5) vorhanden:
- Von der effektiven Basisklasse
C
von bis zuT
jeder Basisklasse vonC
bis zuT
T
. - Von jedem interface_type in
T
. - Von
T
einem beliebigen interface_typeI
, der bereitgestellt wird, gibt es noch keine implizite Verweiskonvertierung vonT
zuI
. - Von einer type_parameter
U
, dieT
T
davonU
abhängt (§15.2.5).Hinweis: Da
T
bekannt ist, dass es sich um einen Verweistyp handelt, ist der Laufzeittyp von Ihnen immer ein Verweistyp, auch wennU
nicht bekannt ist, dass es sich bei derT
Kompilierung um einen Verweistyp handelt. Endnote
Bei einer type_parameterT
, die nicht als Bezugstyp (§15.2.5) bekannt ist, gelten die folgenden Konvertierungen T
zur Kompilierungszeit als Unboxing-Konvertierungen (§10.3.7). Wenn es sich um einen Werttyp handelt, wird die Konvertierung zur Laufzeit T
als Unboxing-Konvertierung ausgeführt. Wenn es sich um einen Verweistyp handelt, wird die Konvertierung zur Laufzeit T
als explizite Referenzkonvertierung oder Identitätskonvertierung ausgeführt.
- Von der effektiven Basisklasse
C
von bis zuT
jeder Basisklasse vonC
bis zuT
T
.Hinweis: C ist eines der Typen
System.Object
,System.ValueType
oderSystem.Enum
(andernfallsT
wäre bekannt als Bezugstyp). Endnote - Von jedem interface_type in
T
.
Für eine type_parameterT
, die nicht als Bezugstyp (§15.2.5) bekannt ist, sind die folgenden expliziten Konvertierungen vorhanden:
- Von
T
bis zu jeder interface_typeI
, sofern noch keine implizite Konvertierung vonT
zuI
. Diese Konvertierung besteht aus einer impliziten Boxumwandlung (§10.2.9) vonT
zuobject
einer expliziten Verweiskonvertierung vonobject
inI
. Wenn es sich umT
einen Werttyp handelt, wird die Konvertierung zur Laufzeit als Boxkonvertierung ausgeführt, gefolgt von einer expliziten Verweiskonvertierung. Wenn es sich um einen Verweistyp handelt, wird die Konvertierung zur LaufzeitT
als explizite Referenzkonvertierung ausgeführt. - Von einem Typparameter
U
bis zurT
Bereitstellung,T
die vonU
(§15.2.5) abhängt. Wenn es sich bei der LaufzeitT
um einen Werttyp handelt undU
ein Bezugstyp ist, wird die Konvertierung als Unboxing-Konvertierung ausgeführt. Zur Laufzeit, wenn beideT
Typen sind undU
Werttypen sind,T
undU
sie sind notwendigerweise derselbe Typ, und es wird keine Konvertierung ausgeführt. Wenn es sich um einen Referenztyp handelt, ist diesU
zur LaufzeitT
auch ein Verweistyp, und die Konvertierung wird als explizite Referenzkonvertierung oder Identitätskonvertierung ausgeführt.
In allen Fällen stellen die Regeln sicher, dass eine Konvertierung als Unboxing-Konvertierung ausgeführt wird, wenn und nur, wenn die Konvertierung zur Laufzeit von einem Verweistyp zu einem Werttyp stammt.
Die oben genannten Regeln lassen keine direkte explizite Konvertierung von einem nicht eingeschränkten Typparameter in einen Nicht-Schnittstellentyp zu, was überraschend sein könnte. Der Grund für diese Regel besteht darin, Verwirrung zu vermeiden und die Semantik solcher Konvertierungen deutlich zu machen.
Beispiel: Betrachten Sie die folgende Deklaration:
class X<T> { public static long F(T t) { return (long)t; // Error } }
Wenn die direkte explizite Konvertierung zulässig
t
long
wäre, könnte man leicht davon ausgehen, dassX<int>.F(7)
dies zurückgegeben7L
würde. Dies wäre jedoch nicht der Fehler, da die standardmäßigen numerischen Konvertierungen nur berücksichtigt werden, wenn die Typen zur Bindungszeit als numerisch bekannt sind. Um die Semantik klar zu machen, muss stattdessen das obige Beispiel geschrieben werden:class X<T> { public static long F(T t) { return (long)(object)t; // Ok, but will only work when T is long } }
Dieser Code wird nun kompiliert, aber die
X<int>.F(7)
Ausführung würde dann zur Laufzeit eine Ausnahme auslösen, da ein Boxedint
nicht direkt in ein Feldlong
konvertiert werden kann.Endbeispiel
10.3.9 Benutzerdefinierte explizite Konvertierungen
Eine benutzerdefinierte explizite Konvertierung besteht aus einer optionalen expliziten Standardkonvertierung, gefolgt von der Ausführung eines benutzerdefinierten impliziten oder expliziten Konvertierungsoperators, gefolgt von einer anderen optionalen expliziten Standardkonvertierung. Die genauen Regeln für die Auswertung benutzerdefinierter expliziter Konvertierungen werden in §10.5.5 beschrieben.
10.4 Standardkonvertierungen
10.4.1 Allgemein
Die Standardkonvertierungen sind die vordefinierten Konvertierungen, die als Teil einer benutzerdefinierten Konvertierung auftreten können.
10.4.2 Implizite Standardkonvertierungen
Die folgenden impliziten Konvertierungen werden als implizite Standardkonvertierungen klassifiziert:
- Identitätskonvertierungen (§10.2.2)
- Implizite numerische Konvertierungen (§10.2.3)
- Implizite nullable Konvertierungen (§10.2.6)
- Nullliteralumwandlungen (§10.2.7)
- Implizite Verweiskonvertierungen (§10.2.8)
- Boxumwandlungen (§10.2.9)
- Implizite Konstantenausdruckkonvertierungen (§10.2.11)
- Implizite Konvertierungen mit Typparametern (§10.2.12)
Die impliziten Standardkonvertierungen schließen explizit benutzerdefinierte implizite Konvertierungen aus.
10.4.3 Explizite Standardkonvertierungen
Die expliziten Standardkonvertierungen sind alle standardmäßigen impliziten Konvertierungen sowie die Teilmenge der expliziten Konvertierungen, für die eine entgegengesetzte implizite Standardkonvertierung vorhanden ist.
Hinweis: Wenn eine implizite Standardkonvertierung von einem Typ in einen Typ
A
B
vorhanden ist, ist eine explizite Standardkonvertierung vom TypA
zum TypB
und vom Typ zum TypB
A
vorhanden. Endnote
10.5 Benutzerdefinierte Konvertierungen
10.5.1 Allgemein
Mit C# können die vordefinierten impliziten und expliziten Konvertierungen durch benutzerdefinierte Konvertierungen erweitert werden. Benutzerdefinierte Konvertierungen werden durch Deklarieren von Konvertierungsoperatoren (§15.10.4) in Klassen- und Strukturtypen eingeführt.
10.5.2 Zulässige benutzerdefinierte Konvertierungen
C# lässt nur bestimmte benutzerdefinierte Konvertierungen zu, die deklariert werden. Insbesondere ist es nicht möglich, eine bereits vorhandene implizite oder explizite Konvertierung neu zu definieren.
Für einen bestimmten Quelltyp und Zieltyp S
T
, wenn S
oder T
nullable Werttypen sind, lassen S₀
und T₀
verweisen Sie auf ihre zugrunde liegenden Typen, andernfalls S₀
und T₀
sind gleich bzwT
. gleichS
. Eine Klasse oder Struktur darf eine Konvertierung von einem Quelltyp in einen Zieltyp S
T
nur deklarieren, wenn alle folgenden Werte zutreffen:
S₀
undT₀
sind unterschiedliche Typen.- Entweder
S₀
oderT₀
ist der Klassen- oder Strukturtyp, in dem die Operatordeklaration stattfindet. - Weder
S₀
T₀
noch ist es ein interface_type. - Ohne benutzerdefinierte Konvertierungen ist eine Konvertierung nicht von
S
zuT
oder vonT
zu .S
Die Einschränkungen, die für benutzerdefinierte Konvertierungen gelten, sind in §15.10.4 angegeben.
10.5.3 Auswertung von benutzerdefinierten Konvertierungen
Eine benutzerdefinierte Konvertierung konvertiert einen Quellausdruck, der möglicherweise einen Quelltyp aufweist, in einen anderen Typ, der als Zieltyp bezeichnet wird. Auswertung eines benutzerdefinierten Konvertierungszentrums zum Auffinden des spezifisch benutzerdefinierten Konvertierungsoperators für den Quellausdruck und Den Zieltyp. Diese Bestimmung ist in mehrere Schritte unterteilt:
- Ermitteln der Klassen und Strukturen, aus denen benutzerdefinierte Konvertierungsoperatoren berücksichtigt werden. Dieser Satz besteht aus dem Quelltyp und seinen Basisklassen, sofern der Quelltyp vorhanden ist, sowie dem Zieltyp und den Basisklassen. Zu diesem Zweck wird davon ausgegangen, dass nur Klassen und Strukturen benutzerdefinierte Operatoren deklarieren können und dass Nicht-Klassentypen keine Basisklassen aufweisen. Wenn der Quell- oder Zieltyp ein Nullwert-Wert-Typ ist, wird stattdessen der zugrunde liegende Typ verwendet.
- Bestimmen Sie anhand dieser Typen, welche benutzerdefinierten und aufgehobenen Konvertierungsoperatoren anwendbar sind. Damit ein Konvertierungsoperator anwendbar ist, muss es möglich sein, eine Standardkonvertierung (§10.4) vom Quellausdruck in den Operandentyp des Operators durchzuführen, und es muss möglich sein, eine Standardkonvertierung vom Ergebnistyp des Operators in den Zieltyp durchzuführen.
- Anhand der Gruppe der anwendbaren benutzerdefinierten Operatoren, die bestimmen, welcher Operator eindeutig die spezifischsten ist. Im Allgemeinen ist der spezifischste Operator der Operator, dessen Operandtyp dem Quellausdruck am nächsten kommt und dessen Ergebnistyp dem Zieltyp "am nächsten" ist. Benutzerdefinierte Konvertierungsoperatoren werden gegenüber aufgehobenen Konvertierungsoperatoren bevorzugt. Die genauen Regeln zum Einrichten des spezifisch benutzerdefinierten Konvertierungsoperators werden in den folgenden Unterclauses definiert.
Nachdem ein bestimmter benutzerdefinierter Konvertierungsoperator identifiziert wurde, umfasst die tatsächliche Ausführung der benutzerdefinierten Konvertierung bis zu drei Schritte:
- Führen Sie bei Bedarf eine Standardkonvertierung vom Quellausdruck in den Operandentyp des benutzerdefinierten oder aufgehobenen Konvertierungsoperators durch.
- Rufen Sie als Nächstes den benutzerdefinierten oder aufgehobenen Konvertierungsoperator auf, um die Konvertierung auszuführen.
- Führen Sie schließlich bei Bedarf eine Standardkonvertierung vom Ergebnistyp des benutzerdefinierten Konvertierungsoperators in den Zieltyp aus.
Die Auswertung einer benutzerdefinierten Konvertierung umfasst niemals mehr als einen benutzerdefinierten oder aufgehobenen Konvertierungsoperator. Mit anderen Worten, eine Konvertierung von Typ S
zu Typ T
führt nie zuerst eine benutzerdefinierte Konvertierung von S
zu X
und dann eine benutzerdefinierte Konvertierung von X
zu T
.
- Genaue Definitionen der Auswertung benutzerdefinierter impliziter oder expliziter Konvertierungen werden in den folgenden Unterlisten angegeben. Die Definitionen verwenden die folgenden Begriffe:
- Wenn eine implizite Standardkonvertierung (§10.4.2) von einem Typ in einen Typ
B
A
vorhanden ist und wenn wederA
B
noch interface_types
sind,A
dann wird davon gesprochen, dassB
sie eingeschlossen werden.B
A
- Wenn eine implizite Standardkonvertierung (§10.4.2) von einem Ausdruck
E
in einen TypB
vorhanden ist, und wenn weder nochB
der Typ vonE
(sofern vorhanden) interface_types
sind,E
wird davon gesprochen, dassB
sie eingeschlossen wird undB
sie umfasstE
. - Der umfassendste Typ in einer Gruppe von Typen ist der typ, der alle anderen Typen in der Gruppe umfasst. Wenn kein einzelner Typ alle anderen Typen umfasst, weist der Satz keinen umfassendsten Typ auf. In intuitiveren Worten ist der umfassendste Typ der "größte" Typ in der Gruppe – der einen Typ, in den jeder der anderen Typen implizit konvertiert werden kann.
- Der umfassendste Typ in einer Gruppe von Typen ist der typ, der von allen anderen Typen in der Gruppe umfasst wird. Wenn kein einzelner Typ von allen anderen Typen umfasst, hat der Satz keinen am meisten eingeschlossenen Typ. In intuitiveren Worten ist der am meisten eingeschlossene Typ der "kleinste" Typ in der Gruppe – der einen Typ, der implizit in jeden der anderen Typen konvertiert werden kann.
10.5.4 Benutzerdefinierte implizite Konvertierungen
Eine benutzerdefinierte implizite Konvertierung von einem Ausdruck E
in einen Typ T
wird wie folgt verarbeitet:
Bestimmen Sie die Typen
S
S₀
undT₀
.- Wenn
E
ein Typ vorhanden ist, lassen Sie unsS
diesen Typ verwenden. - Wenn
S
oderT
nullable Werttypen sind, lassen undTᵢ
sein sie zugrunde liegende Typen, andernfalls lassenSᵢ
Sᵢ
undTᵢ
sein bzwT
. seinS
. - Wenn
Sᵢ
esTᵢ
sich um Typparameter handelt, lassen UndT₀
sein Sie deren effektive Basisklassen, andernfalls lassenS₀
S₀
undT₀
seinSₓ
bzwTᵢ
. sein.
- Wenn
Suchen Sie den Satz von Typen,
D
aus denen benutzerdefinierte Konvertierungsoperatoren berücksichtigt werden. Dieser Satz besteht ausS₀
(sofernS₀
vorhanden und ist eine Klasse oder Struktur), die Basisklassen vonS₀
(sofernS₀
vorhanden und eine Klasse ist) undT₀
(wennT₀
es sich um eine Klasse oder Struktur handelt). Ein Typ wird dem SatzD
nur hinzugefügt, wenn eine Identitätskonvertierung in einen anderen Typ, der bereits in der Gruppe enthalten ist, nicht vorhanden ist.Suchen Sie die Gruppe der anwendbaren benutzerdefinierten und aufgehobenen Konvertierungsoperatoren,
U
. Dieser Satz besteht aus den benutzerdefinierten und aufgehobenen impliziten Konvertierungsoperatoren, die von den Klassen oder Anweisungen deklariert werden, dieD
von einem Typ konvertiert werden, der in einen typumfassendenE
T
Typ umfasst. WennU
die Konvertierung leer ist, ist die Konvertierung nicht definiert, und ein Kompilierungszeitfehler tritt auf.- Wenn
S
vorhanden und eines der Operatoren, die konvertiert werdenS
U
, lautetS
diesSₓ
. Sₓ
Andernfalls ist der umfassendste Typ in der kombinierten Gruppe von Quelltypen der Operatoren inU
. Wenn genau ein am häufigsten eingeschlossener Typ nicht gefunden werden kann, ist die Konvertierung mehrdeutig, und ein Kompilierungszeitfehler tritt auf.
- Wenn
Suchen Sie den spezifischsten Zieltyp(
Tₓ
) der Operatoren inU
:- Wenn einer der Operatoren, in die konvertiert
T
wirdU
, lautetT
diesTₓ
. Tₓ
Andernfalls ist der umfassendste Typ in der kombinierten Gruppe von Zieltypen der Operatoren inU
. Wenn genau ein umfassendster Typ nicht gefunden werden kann, ist die Konvertierung mehrdeutig, und ein Kompilierungszeitfehler tritt auf.
- Wenn einer der Operatoren, in die konvertiert
Suchen Sie den spezifischsten Konvertierungsoperator:
- Wenn
U
genau ein benutzerdefinierter Konvertierungsoperator enthalten ist, der von "inSₓ
Tₓ
" konvertiert wird, ist dies der spezifischste Konvertierungsoperator. - Andernfalls ist dies der spezifischste Konvertierungsoperator, wenn
U
genau ein aufgehobener Konvertierungsoperator enthalten ist, der von in "inSₓ
Tₓ
" konvertiert wird. - Andernfalls ist die Konvertierung mehrdeutig und ein Kompilierungszeitfehler tritt auf.
- Wenn
Wenden Sie schließlich die Konvertierung an:
- Wenn E noch nicht über den Typ
Sₓ
verfügt, wird eine implizite Standardkonvertierung von "inE
Sₓ
" ausgeführt. - Der spezifischste Konvertierungsoperator wird aufgerufen, um von
Sₓ
zuTₓ
konvertieren. - Ist
Tₓ
dies nichtT
der Fehler, wird eine implizite Standardkonvertierung vonTₓ
zuT
"In" ausgeführt.
- Wenn E noch nicht über den Typ
Eine benutzerdefinierte implizite Konvertierung von einem Typ S
in einen Typ T
ist vorhanden, wenn eine benutzerdefinierte implizite Konvertierung von einer Variablen vom Typ S
in T
vorhanden ist.
10.5.5 Benutzerdefinierte explizite Konvertierungen
Eine benutzerdefinierte explizite Konvertierung von einem Ausdruck E
in einen Typ T
wird wie folgt verarbeitet:
- Bestimmen Sie die Typen
S
S₀
undT₀
.- Wenn
E
ein Typ vorhanden ist, lassen Sie unsS
diesen Typ verwenden. - Wenn
S
oderT
nullable Werttypen sind, lassen undTᵢ
sein sie zugrunde liegende Typen, andernfalls lassenSᵢ
Sᵢ
undTᵢ
sein bzwT
. seinS
. - Wenn
Sᵢ
esTᵢ
sich um Typparameter handelt, lassen UndT₀
sein Sie deren effektive Basisklassen, andernfalls lassenS₀
S₀
undT₀
seinSᵢ
bzwTᵢ
. sein.
- Wenn
- Suchen Sie den Satz von Typen,
D
aus denen benutzerdefinierte Konvertierungsoperatoren berücksichtigt werden. Dieser Satz besteht ausS₀
(sofernS₀
vorhanden und ist eine Klasse oder Struktur), die Basisklassen vonS₀
(sofernS₀
vorhanden und eine Klasse ist),T₀
(wennT₀
es sich um eine Klasse oder Struktur handelt) und die Basisklassen vonT₀
(wennT₀
eine Klasse ist).A
type is added to the setD
only if an identity conversion to another type already included in the set doesn't exist. - Suchen Sie die Gruppe der anwendbaren benutzerdefinierten und aufgehobenen Konvertierungsoperatoren,
U
. Dieser Satz besteht aus den benutzerdefinierten und aufgehobenen impliziten oder expliziten Konvertierungsoperatoren, die von den Klassen oder Strukturen deklariert werden, dieD
von einem Typ konvertiert werden, der (sofern vorhanden) in einen typumfassendenE
S
oder eingeschlossenenT
Typ konvertiert wird. WennU
die Konvertierung leer ist, ist die Konvertierung nicht definiert, und ein Kompilierungszeitfehler tritt auf. - Suchen Sie den spezifischsten Quelltyp der
Sₓ
Operatoren inU
:- Wenn S vorhanden ist und eine der Operatoren, die konvertiert werden
U
S
, lautet diesSₓ
S
. - Andernfalls ist einer der Operatoren,
U
die von Typen konvertiert werden, die umfassenE
, derSₓ
am meisten eingeschlossene Typ in der kombinierten Gruppe von Quelltypen dieser Operatoren. Wenn kein umfassendster Typ gefunden werden kann, ist die Konvertierung mehrdeutig, und ein Kompilierungszeitfehler tritt auf. Sₓ
Andernfalls ist der umfassendste Typ in der kombinierten Gruppe von Quelltypen der Operatoren inU
. Wenn genau ein umfassendster Typ nicht gefunden werden kann, ist die Konvertierung mehrdeutig, und ein Kompilierungszeitfehler tritt auf.
- Wenn S vorhanden ist und eine der Operatoren, die konvertiert werden
- Suchen Sie den spezifischsten Zieltyp(
Tₓ
) der Operatoren inU
:- Wenn einer der Operatoren, in die konvertiert
T
wirdU
, lautetT
diesTₓ
. - Andernfalls ist einer der Operatoren, die in
U
Typen konvertiert werden, die vonT
diesen eingeschlossen sind, derTₓ
umfassendste Typ in der kombinierten Gruppe von Zieltypen dieser Operatoren. Wenn genau ein umfassendster Typ nicht gefunden werden kann, ist die Konvertierung mehrdeutig, und ein Kompilierungszeitfehler tritt auf. Tₓ
Andernfalls ist der umfassendste Typ in der kombinierten Gruppe von Zieltypen der Operatoren inU
. Wenn kein umfassendster Typ gefunden werden kann, ist die Konvertierung mehrdeutig, und ein Kompilierungszeitfehler tritt auf.
- Wenn einer der Operatoren, in die konvertiert
- Suchen Sie den spezifischsten Konvertierungsoperator:
- Wenn U genau einen benutzerdefinierten Konvertierungsoperator enthält, der von "in
Sₓ
Tₓ
" konvertiert wird, ist dies der spezifischste Konvertierungsoperator. - Andernfalls ist dies der spezifischste Konvertierungsoperator, wenn
U
genau ein aufgehobener Konvertierungsoperator enthalten ist, der von in "inSₓ
Tₓ
" konvertiert wird. - Andernfalls ist die Konvertierung mehrdeutig und ein Kompilierungszeitfehler tritt auf.
- Wenn U genau einen benutzerdefinierten Konvertierungsoperator enthält, der von "in
- Wenden Sie schließlich die Konvertierung an:
- Wenn
E
der TypSₓ
noch nicht vorhanden ist, wird eine explizite Standardkonvertierung von E inSₓ
ausgeführt. - Der spezifisch benutzerdefinierte Konvertierungsoperator wird aufgerufen, um von
Sₓ
zuTₓ
konvertieren. - Ist
Tₓ
dies nichtT
der Fehler, wird eine explizite Standardkonvertierung von "inTₓ
T
" ausgeführt.
- Wenn
Eine benutzerdefinierte explizite Konvertierung von einem Typ S
in einen Typ T
ist vorhanden, wenn eine benutzerdefinierte explizite Konvertierung von einer Variablen des Typs S
in T
vorhanden ist.
10.6 Konvertierungen mit nullfähigen Typen
10.6.1 Nullfähige Konvertierungen
Nullfähige Konvertierungen ermöglichen vordefinierte Konvertierungen , die auf nicht nullwertigen Werttypen ausgeführt werden, die auch mit nullablen Formen dieser Typen verwendet werden. Für jede der vordefinierten impliziten oder expliziten Konvertierungen, die von einem nicht nullierbaren Werttyp in einen nicht nullablen Werttyp S
T
konvertiert werden (§10.2.2, §10.2.3, §10.2.4, §10.2.11, §10.3.2 und §10.3.3), sind die folgenden Nullwerte vorhanden:
- Eine implizite oder explizite Konvertierung von
S?
zuT?
- Eine implizite oder explizite Konvertierung von
S
zuT?
- Eine explizite Konvertierung von
S?
zuT
.
Eine nullable Konvertierung wird selbst als implizite oder explizite Konvertierung klassifiziert.
Bestimmte nullable Konvertierungen werden als Standardkonvertierungen klassifiziert und können als Teil einer benutzerdefinierten Konvertierung auftreten. Insbesondere werden alle impliziten nullablen Konvertierungen als implizite Standardkonvertierungen (§10.4.2) klassifiziert, und die expliziten nullfähigen Konvertierungen, die die Anforderungen von §10.4.3 erfüllen, werden als standardmäßige explizite Konvertierungen klassifiziert.
Auswertung einer nullfähigen Konvertierung basierend auf einer zugrunde liegenden Konvertierung von S
zu T
Erlösen wie folgt:
- Wenn die Nullwertekonvertierung von
S?
:T?
- Wenn der Quellwert null (
HasValue
Eigenschaft ist ) istfalse
, ist das Ergebnis der Nullwert des TypsT?
. - Andernfalls wird die Konvertierung als entwrapping von
S?
zuS
, gefolgt von der zugrunde liegenden Konvertierung vonS
zuT
, gefolgt von einem Umbruch vonT
zu .T?
- Wenn der Quellwert null (
- Wenn die nullable Konvertierung von
S
zuT?
, wird die Konvertierung als zugrunde liegende Konvertierung vonS
zuT
gefolgt von einem Umbruch vonT
zuT?
. - Wenn die NULL-Konvertierung von
S?
zuT
Null erfolgt, wird die Konvertierung als Entwrapping vonS?
zuS
gefolgt von der zugrunde liegenden Konvertierung vonS
zuT
" ausgewertet.
10.6.2 Angehobene Konvertierungen
Bei einem benutzerdefinierten Konvertierungsoperator, der von einem nicht nullfähigen Werttyp in einen nicht nullablen Werttyp S
T
konvertiert wird, ist ein aufgehobener Konvertierungsoperator vorhanden, der von S?
in T?
. Dieser aufgehobene Konvertierungsoperator führt eine Entwrapping von S?
zu gefolgt von der benutzerdefinierten Konvertierung von zu T
gefolgt von einem Umbruch von T
S
in T?
, mit der Ausnahme, dass ein NULL-Wert S?
direkt in einen NULL-Wert konvertiert wirdT?
.S
Ein angehobener Konvertierungsoperator verfügt über dieselbe implizite oder explizite Klassifizierung wie der zugrunde liegende benutzerdefinierte Konvertierungsoperator.
10.7 Anonyme Funktionskonvertierungen
10.7.1 Allgemein
Ein anonymous_method_expression oder lambda_expression wird als anonyme Funktion klassifiziert (§12.19). Der Ausdruck hat keinen Typ, kann jedoch implizit in einen kompatiblen Delegattyp konvertiert werden. Einige Lambda-Ausdrücke können auch implizit in einen kompatiblen Ausdrucksstrukturtyp konvertiert werden.
Insbesondere ist eine anonyme Funktion F
mit einem Stellvertretungstyp D
kompatibel:
- Wenn
F
ein anonymous_function_signature enthält,D
haben SieF
die gleiche Anzahl von Parametern. - Wenn
F
kein anonymous_function_signature enthalten ist, kannD
es null oder mehr Parameter eines Typs geben, sofern kein Parameter einesD
Ausgabeparameters ist. - Wenn
F
eine explizit eingegebene Parameterliste vorhanden ist, verfügt jeder Parameter inD
denselben Modifizierern wie der entsprechende Parameter inF
und eine Identitätskonvertierung ist zwischen dem entsprechenden Parameter inF
. - Wenn
F
eine implizit typierte Parameterliste vorhanden ist,D
gibt es keine Referenz- oder Ausgabeparameter. - Wenn der Textkörper
F
ein Ausdruck ist und entwederD
einen ungültigen Rückgabetyp aufweist oderF
asynchron ist undD
einen«TaskType»
Rückgabetyp (§15.15.1) aufweist, wird jeder Parameter desF
entsprechenden Parameters imD
TextkörperF
als gültiger Ausdruck (w.r.t §12) angegeben, der als statement_expression (§13.7) zulässig wäre. - Wenn der Textkörper
F
ein Block ist und entwederD
einen ungültigen Rückgabetyp aufweist oderF
asynchron ist undD
einen«TaskType»
Rückgabetyp aufweist, wird bei jedem ParameterF
des entsprechenden Parameters imD
TextkörperF
ein gültiger Block (w.r.t §13.3) angegeben, in dem keinereturn
Anweisung einen Ausdruck angibt. - Wenn der Textkörper
F
ein Ausdruck ist und entwederF
nicht asynchron ist undD
einen Nicht-Rückgabetypvoid
aufweist oderF
asynchron ist undD
einen«TaskType»<T>
RückgabetypT
(§15.15.1) aufweist, ist derF
D
TextkörperF
eines gültigen Ausdrucks (w.r.t §12), der implizit zu konvertierbarT
ist. - Wenn der Textkörper
F
ein Block ist und entwederF
nicht asynchron ist undD
über einen nicht ungültigen RückgabetypT
verfügt oderF
asynchron ist undD
einen«TaskType»<T>
Rückgabetyp aufweist, gibt jeder ParameterF
den Typ des entsprechenden Parameters inD
, ist der TextkörperF
eines gültigen Anweisungsblocks (w.r.t §13.3) mit einem nicht erreichbaren Endpunkt, in dem jede Rückgabe anweisung einen Ausdruck angibt, der implizit zu konvertierbarT
ist.
Beispiel: Die folgenden Beispiele veranschaulichen diese Regeln:
delegate void D(int x); D d1 = delegate { }; // Ok D d2 = delegate() { }; // Error, signature mismatch D d3 = delegate(long x) { }; // Error, signature mismatch D d4 = delegate(int x) { }; // Ok D d5 = delegate(int x) { return; }; // Ok D d6 = delegate(int x) { return x; }; // Error, return type mismatch delegate void E(out int x); E e1 = delegate { }; // Error, E has an output parameter E e2 = delegate(out int x) { x = 1; }; // Ok E e3 = delegate(ref int x) { x = 1; }; // Error, signature mismatch delegate int P(params int[] a); P p1 = delegate { }; // Error, end of block reachable P p2 = delegate { return; }; // Error, return type mismatch P p3 = delegate { return 1; }; // Ok P p4 = delegate { return "Hello"; }; // Error, return type mismatch P p5 = delegate(int[] a) // Ok { return a[0]; }; P p6 = delegate(params int[] a) // Error, params modifier { return a[0]; }; P p7 = delegate(int[] a) // Error, return type mismatch { if (a.Length > 0) return a[0]; return "Hello"; }; delegate object Q(params int[] a); Q q1 = delegate(int[] a) // Ok { if (a.Length > 0) return a[0]; return "Hello"; };
Endbeispiel
Beispiel: Die folgenden Beispiele verwenden einen generischen Delegattyp
Func<A,R>
, der eine Funktion darstellt, die ein Argument vom TypA
verwendet und einen Wert vom TypR
zurückgibt:delegate R Func<A,R>(A arg);
In den Aufgaben
Func<int,int> f1 = x => x + 1; // Ok Func<int,double> f2 = x => x + 1; // Ok Func<double,int> f3 = x => x + 1; // Error Func<int, Task<int>> f4 = async x => x + 1; // Ok
Der Parameter und die Rückgabetypen jeder anonymen Funktion werden vom Typ der Variablen bestimmt, der die anonyme Funktion zugewiesen ist.
Die erste Zuweisung konvertiert die anonyme Funktion erfolgreich in den Delegattyp
Func<int,int>
, da beix
angabe des Typsint
x + 1
ein gültiger Ausdruck ist, der implizit in typkonvertierbarint
ist.Ebenso konvertiert die zweite Zuordnung die anonyme Funktion erfolgreich in den Stellvertretungstyp Func<int, doppelt> , da das Ergebnis von
x + 1
(vom Typint
) implizit in Typdouble
konvertiert wird.Die dritte Zuordnung ist jedoch ein Kompilierungszeitfehler, da das
x
Ergebnis desx + 1
Typs (des Typsdouble
double
) nicht implizit in typmäßigint
konvertierbar ist.Die vierte Zuordnung konvertiert die anonyme asynchrone Funktion erfolgreich in den Delegattyp
Func<int, Task<int>>
, da das Ergebnis vonx + 1
(vom Typint
) implizit in den effektiven Rückgabetypint
der asynchronen Lambda-Funktion konvertiert wird, die einen RückgabetypTask<int>
aufweist.Endbeispiel
Ein Lambda-Ausdruck F
ist mit einem Ausdrucksstrukturtyp Expression<D>
kompatibel, wenn F
er mit dem Delegattyp D
kompatibel ist. Dies gilt nicht für anonyme Methoden, nur Lambda-Ausdrücke.
Anonyme Funktionen können die Überlastungsauflösung beeinflussen und an der Typeinleitung teilnehmen. Weitere Details finden Sie unter §12.6 .
10.7.2 Auswertung anonymer Funktionskonvertierungen in Stellvertretungstypen
Die Konvertierung einer anonymen Funktion in einen Delegattyp erzeugt eine Delegateninstanz, die auf die anonyme Funktion und den (möglicherweise leeren) Satz erfasster äußerer Variablen verweist, die zum Zeitpunkt der Auswertung aktiv sind. Wenn die Stellvertretung aufgerufen wird, wird der Textkörper der anonymen Funktion ausgeführt. Der Code im Textkörper wird mithilfe des Satzes erfasster äußerer Variablen ausgeführt, auf die vom Delegaten verwiesen wird. Ein delegate_creation_expression (§12.8.17.6) kann als alternative Syntax zum Konvertieren einer anonymen Methode in einen Delegatentyp verwendet werden.
Die Aufrufliste eines aus einer anonymen Funktion erzeugten Delegaten enthält einen einzelnen Eintrag. Die genaue Zielobjekt- und Zielmethode des Delegaten sind nicht angegeben. Insbesondere wird nicht angegeben, ob das Zielobjekt des Delegaten , der this
Wert des eingeschlossenen Funktionselements oder eines anderen Objekts istnull
.
Konvertierungen semantischer identischer anonymer Funktionen mit demselben (möglicherweise leeren) Satz erfasster äußerer Variableninstanzen in dieselben Delegattypen sind zulässig (aber nicht erforderlich), um dieselbe Delegateninstanz zurückzugeben. Der Begriff semantisch identisch wird hier verwendet, um zu bedeuten, dass die Ausführung der anonymen Funktionen in allen Fällen dieselben Effekte mit denselben Argumenten erzeugt. Diese Regel lässt die Optimierung von Code wie dem folgenden zu.
delegate double Function(double x);
class Test
{
static double[] Apply(double[] a, Function f)
{
double[] result = new double[a.Length];
for (int i = 0; i < a.Length; i++)
{
result[i] = f(a[i]);
}
return result;
}
static void F(double[] a, double[] b)
{
a = Apply(a, (double x) => Math.Sin(x));
b = Apply(b, (double y) => Math.Sin(y));
...
}
}
Da die beiden anonymen Funktionsdelegatten den gleichen (leeren) Satz von erfassten äußeren Variablen aufweisen und da die anonymen Funktionen semantisch identisch sind, darf der Compiler die Stellvertretungen auf dieselbe Zielmethode verweisen lassen. Tatsächlich ist es dem Compiler gestattet, die gleiche Delegateninstanz aus beiden anonymen Funktionsausdrücken zurückzugeben.
10.7.3 Auswertung von Lambda-Ausdruckskonvertierungen in Ausdrucksstrukturtypen
Die Konvertierung eines Lambda-Ausdrucks in einen Ausdrucksstrukturtyp erzeugt eine Ausdrucksstruktur (§8.6). Genauer gesagt erzeugt die Auswertung der Lambda-Ausdruckskonvertierung eine Objektstruktur, die die Struktur des Lambda-Ausdrucks selbst darstellt.
Nicht jeder Lambda-Ausdruck kann in Ausdrucksstrukturtypen konvertiert werden. Die Konvertierung in einen kompatiblen Delegattyp ist immer vorhanden, kann jedoch aus implementierungsdefinierten Gründen zur Kompilierungszeit fehlschlagen.
Hinweis: Häufige Gründe für einen Lambda-Ausdruck, der nicht in einen Ausdrucksstrukturtyp konvertiert werden kann, umfassen:
- Er hat einen Blocktext
- Er verfügt über den
async
Modifizierer- Er enthält einen Zuordnungsoperator.
- Enthält einen Ausgabe- oder Referenzparameter.
- Es enthält einen dynamisch gebundenen Ausdruck.
Endnote
10.8 Methodengruppenkonvertierungen
Eine implizite Konvertierung besteht aus einer Methodengruppe (§12.2) in einen kompatiblen Delegattyp (§20.4). Wenn D
es sich um einen Delegattyp handelt und E
ein Ausdruck ist, der als Methodengruppe klassifiziert wird, ist er D
nur dann E
kompatibel, wenn E
mindestens eine Methode enthalten ist, die in der normalen Form (§12.6.4.2) für jede Argumentliste (§12.6.2) mit Typen und Modifizierern kompatibel ist, die den Parametertypen und Modifizierern entsprechen D
, wie im Folgenden beschrieben.
Die Kompilierungszeitanwendung der Konvertierung von einer Methodengruppe E
in einen Delegattyp D
wird im Folgenden beschrieben.
- Eine einzelne Methode
M
wird ausgewählt, die einem Methodenaufruf (§12.8.10.2) des FormularsE(A)
entspricht, mit den folgenden Änderungen:- Die Argumentliste
A
ist eine Liste von Ausdrücken, die jeweils als Variable klassifiziert sind, und mit dem Typ und Modifizierer (in
,out
oderref
) des entsprechenden Parameters im parameter_list vonD
– mit Ausnahme von Parametern vom Typdynamic
, wobei der entsprechende Ausdruck den Typobject
anstelle vondynamic
. - Die in Betracht gezogenen Kandidatenmethoden sind nur die Methoden, die in normaler Form anwendbar sind und keine optionalen Parameter weglassen (§12.6.4.2). Daher werden Kandidatenmethoden ignoriert, wenn sie nur in ihrer erweiterten Form anwendbar sind, oder wenn mindestens ein optionaler Parameter keinen entsprechenden Parameter enthält.
D
- Die Argumentliste
- Eine Konvertierung gilt als vorhanden, wenn der Algorithmus von §12.8.10.2 eine einzige beste Methode
M
erzeugt, die kompatibel ist (§20.4) mitD
. - Wenn es sich bei der ausgewählten Methode
M
um eine Instanzmethode handelt, bestimmt der instanzbezogeneE
Ausdruck das Zielobjekt des Delegaten. - Wenn es sich bei der ausgewählten Methode um eine Erweiterungsmethode
M
handelt, die durch einen Memberzugriff für einen Instanzausdruck gekennzeichnet wird, bestimmt dieser Instanzausdruck das Zielobjekt des Delegaten. - Das Ergebnis der Konvertierung ist ein Wert vom Typ
D
, nämlich ein Delegat, der auf die ausgewählte Methode und das Zielobjekt verweist.
Beispiel: Im folgenden Beispiel werden Methodengruppenkonvertierungen veranschaulicht:
delegate string D1(object o); delegate object D2(string s); delegate object D3(); delegate string D4(object o, params object[] a); delegate string D5(int i); class Test { static string F(object o) {...} static void G() { D1 d1 = F; // Ok D2 d2 = F; // Ok D3 d3 = F; // Error – not applicable D4 d4 = F; // Error – not applicable in normal form D5 d5 = F; // Error – applicable but not compatible } }
Die Zuordnung zum
d1
impliziten Konvertieren der MethodengruppeF
in einen Wert vom TypD1
.Die Zuordnung zeigt,
d2
wie es möglich ist, einen Delegaten an eine Methode zu erstellen, die weniger abgeleitete (kontravariant)-Parametertypen und einen abgeleiteten (kovarianten) Rückgabetyp aufweist.Die Zuordnung zeigt,
d3
wie keine Konvertierung vorhanden ist, wenn die Methode nicht anwendbar ist.Die Zuordnung, die
d4
zeigt, wie die Methode in normaler Form anwendbar sein muss.Die Zuordnung, die
d5
zeigt, wie Parameter- und Rückgabetypen des Delegaten und der Methode nur für Referenztypen unterschiedlich sein dürfen.Endbeispiel
Wie bei allen anderen impliziten und expliziten Konvertierungen kann der Umwandlungsoperator verwendet werden, um eine bestimmte Konvertierung explizit auszuführen.
Beispiel: Das Beispiel
object obj = new EventHandler(myDialog.OkClick);
könnte stattdessen geschrieben werden
object obj = (EventHandler)myDialog.OkClick;
Endbeispiel
Eine Methodengruppenkonvertierung kann auf eine generische Methode verweisen, entweder durch explizites Angeben von Typargumenten innerhalb E
oder über typinferenz (§12.6.3). Wenn typinference verwendet wird, werden die Parametertypen des Delegaten als Argumenttypen im Ableitungsprozess verwendet. Der Rückgabetyp der Stellvertretung wird nicht für Rückschlüsse verwendet. Unabhängig davon, ob die Typargumente angegeben oder abgeleitet werden, sind sie Teil des Methodengruppenkonvertierungsprozesses; Dies sind die Typargumente, die verwendet werden, um die Zielmethode aufzurufen, wenn der resultierende Delegat aufgerufen wird.
Beispiel:
delegate int D(string s, int i); delegate int E(); class X { public static T F<T>(string s, T t) {...} public static T G<T>() {...} static void Main() { D d1 = F<int>; // Ok, type argument given explicitly D d2 = F; // Ok, int inferred as type argument E e1 = G<int>; // Ok, type argument given explicitly E e2 = G; // Error, cannot infer from return type } }
Endbeispiel
Methodengruppen können die Überlastungsauflösung beeinflussen und an der Typeinleitung teilnehmen. Weitere Details finden Sie unter §12.6 .
Die Laufzeitauswertung einer Methodengruppenkonvertierung wird wie folgt fortgesetzt:
- Wenn die zur Kompilierungszeit ausgewählte Methode eine Instanzmethode ist oder es sich um eine Erweiterungsmethode handelt, auf die als Instanzmethode zugegriffen wird, wird das Zielobjekt des Delegaten aus dem Instanzausdruck bestimmt, der mit
E
:- Der Instanzausdruck wird ausgewertet. Wenn diese Auswertung eine Ausnahme verursacht, werden keine weiteren Schritte ausgeführt.
- Wenn der Instanzausdruck einer reference_type ist, wird der vom Instanzausdruck berechnete Wert zum Zielobjekt. Wenn es sich bei der ausgewählten Methode um eine Instanzmethode handelt und das Zielobjekt lautet
null
, wird einSystem.NullReferenceException
Fehler ausgelöst, und es werden keine weiteren Schritte ausgeführt. - Wenn der Instanzausdruck einer value_type ist, wird ein Boxvorgang (§10.2.9) ausgeführt, um den Wert in ein Objekt zu konvertieren, und dieses Objekt wird zum Zielobjekt.
- Andernfalls ist die ausgewählte Methode Teil eines statischen Methodenaufrufs, und das Zielobjekt des Delegaten ist
null
. - Eine Delegatinstanz des Delegatentyps
D
wird mit einem Verweis auf die Methode abgerufen, die zur Kompilierungszeit bestimmt wurde, und einen Verweis auf das oben berechnete Zielobjekt:- Die Konvertierung ist zulässig (aber nicht erforderlich), um eine vorhandene Delegatinstanz zu verwenden, die bereits diese Verweise enthält.
- Wenn eine vorhandene Instanz nicht wiederverwendet wurde, wird ein neues erstellt (§20.5). Wenn nicht genügend Arbeitsspeicher verfügbar ist, um die neue Instanz zuzuweisen, wird ein
System.OutOfMemoryException
Fehler ausgelöst. Andernfalls wird die Instanz mit den angegebenen Verweisen initialisiert.
ECMA C# draft specification