Freigeben über


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 Typ long ist implizit, sodass Ausdrücke des Typs int implizit als Typ longbehandelt werden können. Die entgegengesetzte Konvertierung von Typ long zu Typ intist 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 und T, für jeden Typ T.
  • Zwischen T und T? für jeden Referenztyp T.
  • Zwischen object und dynamic.
  • 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 t1t2 und t3 alle haben zwei Elemente: eine int gefolgt von einer string. Tupel-Elementtypen können sich selbst durch Tupel, wie in t4, t5, und t6. Eine Identitätskonvertierung besteht zwischen jedem Paar der entsprechenden Elementtypen, einschließlich geschachtelter Tupel, daher ist eine Identitätskonvertierung zwischen den Typen von Tupeln t4, t5und t6.

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 bis short, int, long, , float, , doubleoder decimal.
  • Von byte bis short, ushort, int, uint, long, , ulong, , floatoder decimaldouble.
  • Von short bis int, long, float, , doubleoder decimal.
  • Von ushort bis int, uint, long, ulong, , float, , , doubleoder decimal.
  • Von int bis long, float, , doubleoder decimal.
  • Von uint bis long, ulong, float, , doubleoder decimal.
  • Von long bis float, double, oder decimal.
  • Von ulong bis float, double, oder decimal.
  • Von char bis ushort, int, uint, long, , ulong, float, , oder doubledecimal.
  • Von float in double.

Konvertierungen von int, uintoder 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 und dynamic.
  • Von jeder class_type bis zu jeder class_type S T , bereitgestellt S 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 , bereitgestellt S wird von .T
  • Von einem array_type S mit einem Elementtyp Sᵢ bis zu einem array_type T mit einem Elementtyp Tᵢ, vorausgesetzt, alle folgenden Sind erfüllt:
    • S und T unterscheiden sich nur im Elementtyp. Mit anderen Worten, S und T sie haben dieselbe Anzahl von Dimensionen.
    • Eine implizite Verweiskonvertierung ist von Sᵢ zu Tᵢ.
  • Von einem eindimensionalen Arraytyp S[] in System.Collections.Generic.IList<T>System.Collections.Generic.IReadOnlyList<T>, und deren Basisschnittstellen, vorausgesetzt, es gibt eine implizite Identität oder Referenzkonvertierung von S zu T.
  • 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_typeT, wenn sie über eine implizite Identität oder Einen Verweiskonvertierung in eine reference_type T₀ verfügt und T₀ eine Identitätskonvertierung in T.
  • 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 Delegattyp T₀ verfügt und T₀ varianzkonvertierbar (§18.2.3.3) in T.
  • 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_typeI, sodass es eine Boxumwandlung von der non_nullable_value_type in eine andere interface_type I₀gibt und I₀ eine Identitätskonvertierung aufweistI.
  • Von jedem non_nullable_value_type auf jede interface_typeI, sodass es eine Boxumwandlung von der non_nullable_value_type in eine andere interface_type I₀ gibt und I₀ varianzkonvertierbar (§18.2.3.3) in I.
  • 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 Schnittstelle Imit einer boxenden Klasse, die aufgerufen wird S_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 Ausdruck new S_Boxing(v) auszuführen und die resultierende Instanz als Wert des Zieltyps der Konvertierung zurückzugeben. So werden die Aussagen

S 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 Laufzeittyp Sauf, und ein Laufzeittyp überprüft den is 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 Typ objectbetrachtet 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 Wert p kopiert wird. Stattdessen Point wurde der class Wert 20 ausgegeben, da p er box 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 Tvorhanden. 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 objectwerden.

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 (dstring) in den Zieltyp gesucht. Eine Konvertierung wird gefunden string , aber nicht in int.

Endbeispiel

10.2.11 Konvertierung impliziter Konstantenausdruck

Eine implizite Konstantenausdruckkonvertierung ermöglicht die folgenden Konvertierungen:

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 Basisklasse C, von T bis zu jeder Basisklasse von C, und von T bis zu jeder Schnittstelle, die von C.
  • Von T bis zu einem interface_type I im Teffektiven Schnittstellensatz und von T einer beliebigen Basisschnittstelle.I
  • Von T einem TypparameterU, der von T (§15.2.5) abhängt U .

    Hinweis: Da T bekannt ist, dass es sich um einen Verweistyp handelt, ist der TLaufzeittyp U immer ein Verweistyp, auch wenn U 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 Basisklasse C, von T bis zu jeder Basisklasse von C, und von T bis zu jeder Schnittstelle, die von C.

    Hinweis: C Ist einer der Typen System.Object, System.ValueTypeoder System.Enum (andernfalls T wäre bekannt als Bezugstyp). Endnote

  • Von T bis zu einem interface_type I im Teffektiven Schnittstellensatz und von T 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 Tsind die folgenden weiteren impliziten Konvertierungen vorhanden:

  • Von T zu einem Verweistyp S , wenn er über eine implizite Konvertierung in einen Verweistyp S₀ verfügt und S₀ eine Identitätskonvertierung in S. Zur Laufzeit wird die Konvertierung auf die gleiche Weise ausgeführt wie die Konvertierung in S₀.
  • Von T einem Schnittstellentyp I , wenn er über eine implizite Konvertierung in einen Schnittstellentyp I₀verfügt und I₀ varianzkonvertierbar in I (§18.2.3.3) ist. Wenn es sich um einen Werttyp handelt, wird die Konvertierung zur Laufzeit T 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 Tvorhanden ist. Die Konvertierung erfolgt durch Erstellen einer Instanz des Tentsprechenden 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, t2und t4 t5 alle gültig, da implizite Konvertierungen aus den Elementausdrücken in die entsprechenden Elementtypen vorhanden sind. Die Deklaration von t3 ist ungültig, da keine Konvertierung von null zu int. Die Deklaration führt t5 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 bis byte, ushort, uint, , ulongoder char.
  • Von byte bis sbyte oder char.
  • Von short bis sbyte, byte, ushort, , uint, , ulongoder char.
  • Von ushort bis sbyte, byte, , shortoder char.
  • Von int bis sbyte, byte, short, ushort, , uint, , , ulongoder char.
  • Von uint bis sbyte, byte, short, , ushort, , intoder char.
  • Von long bis sbyte, byte, short, ushort, , int, uint, , oder ulongchar.
  • Von ulong bis sbyte, byte, short, ushort, , int, uint, , oder longchar.
  • Von char bis sbyte, byte, oder short.
  • Von float zu sbyte, byte, short, ushort, int, , uint, , long, ulong, , oder chardecimal.
  • Von double zu sbyte, byte, short, ushort, int, , uint, long, charulong, , , oder floatdecimal.
  • Von decimal zu sbyte, byte, short, ushort, int, , uint, long, charulong, , , oder floatdouble.

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 einen System.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.
  • 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 ein System.OverflowException Fehler ausgelöst.
  • Bei einer Konvertierung von float oder double 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.
    • 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.
  • Bei einer Konvertierung von double in " in float" wird der double Wert auf den nächsten float Wert gerundet. Wenn der double Wert zu klein ist, um als ein floatWert darzustellen, wird das Ergebnis null mit demselben Vorzeichen wie der Wert. Wenn die Größe des double Werts zu groß ist, um als ein floatWert darzustellen, wird das Ergebnis unendlich mit demselben Zeichen wie der Wert. Wenn der double Wert "NaN" lautet, lautet das Ergebnis auch "NaN".
  • Bei einer Konvertierung von float oder double in " in decimal" wird der Quellwert in Darstellung konvertiert und bei Bedarf auf die nächste Zahl gerundet decimal (§8.3.8).
    • Wenn der Quellwert zu klein ist, um als ein decimalObjekt darzustellen, wird das Ergebnis null, wobei das Vorzeichen des ursprünglichen Werts beibehalten wird, wenn decimal signierte Nullwerte unterstützt werden.
    • Wenn die Größe des Quellwerts zu groß ist, um ihn als decimalUnendlichkeit 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.
  • Bei einer Konvertierung von decimal zu float oder double, wird der decimal Wert auf den nächsten double oder float 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 Bereich float und doublesein, ist jedoch nicht garantiert. Für decimal Darstellungen ohne Infinitäten oder NaN-Werte und mit einem Bereich kleiner als float, wird das Ergebnis einer Konvertierung von decimal entweder in oder float nie unendlich oder double NaN sein. Endnote

10.3.3 Explizite Enumerationskonvertierungen

Die expliziten Enumerationskonvertierungen sind:

  • Von sbyte, , byte, short, ushort, uintint, long, ulong, char, float, , , doubleoder decimal zu einem beliebigen enum_type.
  • Von jeder enum_type in sbyte, byte, short, intushort, , uint, , long, ulong, char, float, , , , oder doubledecimal.
  • 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 von int, wird eine Konvertierung von E in byte eine explizite numerische Konvertierung (§10.3.2) von int in byte, und eine Konvertierung von byte zu E " wird als implizite numerische Konvertierung (§10.2.3) von byte zu intverarbeitet . 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 , bereitgestellt S ist eine Basisklasse von .T
  • Von jedem class_type S bis zu einer interface_type T, vorausgesetzt S , nicht versiegelt und bereitgestellt S wird nicht implementiert T.
  • Von jedem interface_type bis zu jeder class_type T S, sofern nicht T versiegelt oder bereitgestellt T wird.S
  • Von jedem interface_typeS bis zu einer interface_type T, vorausgesetzt S , wird nicht von T.
  • Von einem array_type S mit einem Elementtyp Sᵢ bis zu einem array_type T mit einem Elementtyp Tᵢ, vorausgesetzt, alle folgenden Sind erfüllt:
    • S und T unterscheiden sich nur im Elementtyp. Mit anderen Worten, S und T sie haben dieselbe Anzahl von Dimensionen.
    • Eine explizite Verweiskonvertierung ist von Sᵢ zu Tᵢ.
  • Von System.Array und den von ihr implementierten Schnittstellen bis zu jedem array_type.
  • Von einer eindimensionalen array_type S[] in System.Collections.Generic.IList<T>System.Collections.Generic.IReadOnlyList<T>, und deren Basisschnittstellen, vorausgesetzt, es gibt eine Identitätskonvertierung oder explizite Verweiskonvertierung von S zu T.
  • Von System.Collections.Generic.IList<S>, System.Collections.Generic.IReadOnlyList<S>und deren Basisschnittstellen zu einem eindimensionalen Arraytyp T[], vorausgesetzt, es gibt eine Identitätskonvertierung oder explizite Verweiskonvertierung von S zu T.
  • Von System.Delegate und den Schnittstellen, die sie für alle delegate_type implementiert.
  • Von einem Verweistyp S zu einem Verweistyp T , wenn er über eine explizite Verweiskonvertierung von S einem Verweistyp in einen Verweistyp T₀ verfügt und T₀ eine Identitätskonvertierung von T₀ zu T.
  • Von einem Verweistyp S auf eine Schnittstelle oder einen DelegattypT, wenn es eine explizite Verweiskonvertierung von S einer Schnittstelle oder einem Delegattyp T₀ gibt und entweder T₀ varianzkonvertierbar in §18.2.3.3 ist oder T varianzkonvertierbar T T₀ ist.
  • Von D<S₁...Sᵥ> wo D<T₁...Tᵥ> D<X₁...Xᵥ> aus ein generischer Delegattyp, D<S₁...Sᵥ> nicht kompatibel mit oder identisch D<T₁...Tᵥ>mit , und für jeden Typparameter Xᵢ der D folgenden Haltebereiche:
    • Wenn Xᵢ dies invariant ist, ist dies Sᵢ identisch mit Tᵢ.
    • Wenn Xᵢ kovariant ist, gibt es eine Identitätskonvertierung, implizite Verweiskonvertierung oder explizite Verweiskonvertierung von Sᵢ in Tᵢ.
    • Wenn Xᵢ dies kontravariant ist, Sᵢ sind sie Tᵢ entweder identisch oder beide Bezugstypen.
  • 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 nullder 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 Tvorhanden ist. Die Konvertierung erfolgt durch Erstellen einer Instanz des Tentsprechenden 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_typeI, in der eine Posteingangskonvertierung von einem interface_type in den non_nullable_value-Typ I₀ und eine Identitätskonvertierung von I in .I₀
  • Von jedem interface_type auf jede non_nullable_value_typeI, in der eine Unboxing-Konvertierung von einer interface_type I₀ in die non_nullable_value_type vorhanden ist und entweder I₀ variance_convertible in I oder I varianzkonvertierbar ist I₀ (§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).valuedurch eine Unboxumwandlung eines Objektfelds in ein value_typeS. So werden die Aussagen

object 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 zu T jeder Basisklasse von C bis zu TT .
  • Von jedem interface_type in T.
  • Von T einem beliebigen interface_typeI, der bereitgestellt wird, gibt es noch keine implizite Verweiskonvertierung von T zu I.
  • Von einer type_parameterU, die T T davon U 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 wenn U nicht bekannt ist, dass es sich bei der TKompilierung 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 zu T jeder Basisklasse von C bis zu TT .

    Hinweis: C ist eines der Typen System.Object, System.ValueTypeoder System.Enum (andernfalls T 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 von T zu I. Diese Konvertierung besteht aus einer impliziten Boxumwandlung (§10.2.9) von T zu object einer expliziten Verweiskonvertierung von object in I. Wenn es sich um T 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 Laufzeit T als explizite Referenzkonvertierung ausgeführt.
  • Von einem Typparameter U bis zur T Bereitstellung, T die von U (§15.2.5) abhängt. Wenn es sich bei der Laufzeit T um einen Werttyp handelt und U ein Bezugstyp ist, wird die Konvertierung als Unboxing-Konvertierung 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 um einen Referenztyp handelt, ist dies U zur Laufzeit T 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, dass X<int>.F(7) dies zurückgegeben 7Lwü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 Boxed int nicht direkt in ein Feld longkonvertiert 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 Bvorhanden ist, ist eine explizite Standardkonvertierung vom Typ A zum Typ B und vom Typ zum Typ B Avorhanden. 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₀ und T₀ sind unterschiedliche Typen.
  • Entweder S₀ oder T₀ 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 zu T oder von T 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 BA vorhanden ist und wenn weder A B noch interface_type s 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 Typ Bvorhanden ist, und wenn weder noch B der Typ von E (sofern vorhanden) interface_type s sind, E wird davon gesprochen, dassB sie eingeschlossen wird und B 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 SS₀ und T₀.

    • Wenn E ein Typ vorhanden ist, lassen Sie uns S diesen Typ verwenden.
    • Wenn S oder T nullable Werttypen sind, lassen und Tᵢ sein sie zugrunde liegende Typen, andernfalls lassen Sᵢ Sᵢ und Tᵢ sein bzwT. seinS.
    • Wenn Sᵢ es Tᵢ sich um Typparameter handelt, lassen Und T₀ sein Sie deren effektive Basisklassen, andernfalls lassen S₀ S₀ und T₀ sein Sₓ bzwTᵢ. sein.
  • Suchen Sie den Satz von Typen, Daus denen benutzerdefinierte Konvertierungsoperatoren berücksichtigt werden. Dieser Satz besteht aus S₀ (sofern S₀ vorhanden und ist eine Klasse oder Struktur), die Basisklassen von S₀ (sofern S₀ vorhanden und eine Klasse ist) und T₀ (wenn T₀ es sich um eine Klasse oder Struktur handelt). Ein Typ wird dem Satz D 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, die D von einem Typ konvertiert werden, der in einen typumfassenden E TTyp umfasst. Wenn U die Konvertierung leer ist, ist die Konvertierung nicht definiert, und ein Kompilierungszeitfehler tritt auf.

    • Wenn S vorhanden und eines der Operatoren, die konvertiert werdenSU, lautet Sdies Sₓ .
    • Sₓ Andernfalls ist der umfassendste Typ in der kombinierten Gruppe von Quelltypen der Operatoren in U. Wenn genau ein am häufigsten eingeschlossener Typ nicht gefunden werden kann, ist die Konvertierung mehrdeutig, und ein Kompilierungszeitfehler tritt auf.
  • Suchen Sie den spezifischsten Zieltyp( Tₓ) der Operatoren in U:

    • Wenn einer der Operatoren, in die konvertiert TwirdU, lautet Tdies Tₓ .
    • Tₓ Andernfalls ist der umfassendste Typ in der kombinierten Gruppe von Zieltypen der Operatoren in U. Wenn genau ein umfassendster Typ nicht gefunden werden kann, ist die Konvertierung mehrdeutig, und ein Kompilierungszeitfehler tritt auf.
  • Suchen Sie den spezifischsten Konvertierungsoperator:

    • Wenn U genau ein benutzerdefinierter Konvertierungsoperator enthalten ist, 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 "in Sₓ Tₓ" konvertiert wird.
    • Andernfalls ist die Konvertierung mehrdeutig und ein Kompilierungszeitfehler tritt auf.
  • Wenden Sie schließlich die Konvertierung an:

    • Wenn E noch nicht über den Typ Sₓverfügt, wird eine implizite Standardkonvertierung von "in E Sₓ " ausgeführt.
    • Der spezifischste Konvertierungsoperator wird aufgerufen, um von Sₓ zu Tₓkonvertieren.
    • Ist Tₓ dies nicht Tder Fehler, wird eine implizite Standardkonvertierung von Tₓ zu T "In" ausgeführt.

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 Tvorhanden 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 SS₀ und T₀.
    • Wenn E ein Typ vorhanden ist, lassen Sie uns S diesen Typ verwenden.
    • Wenn S oder T nullable Werttypen sind, lassen und Tᵢ sein sie zugrunde liegende Typen, andernfalls lassen Sᵢ Sᵢ und Tᵢ sein bzwT. seinS.
    • Wenn Sᵢ es Tᵢ sich um Typparameter handelt, lassen Und T₀ sein Sie deren effektive Basisklassen, andernfalls lassen S₀ S₀ und T₀ sein Sᵢ bzwTᵢ. sein.
  • Suchen Sie den Satz von Typen, Daus denen benutzerdefinierte Konvertierungsoperatoren berücksichtigt werden. Dieser Satz besteht aus S₀ (sofern S₀ vorhanden und ist eine Klasse oder Struktur), die Basisklassen von S₀ (sofern S₀ vorhanden und eine Klasse ist), T₀ (wenn T₀ es sich um eine Klasse oder Struktur handelt) und die Basisklassen von T₀ (wenn T₀ eine Klasse ist). A type is added to the set D 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, die D von einem Typ konvertiert werden, der (sofern vorhanden) in einen typumfassenden E S oder eingeschlossenen TTyp konvertiert wird. Wenn U die Konvertierung leer ist, ist die Konvertierung nicht definiert, und ein Kompilierungszeitfehler tritt auf.
  • Suchen Sie den spezifischsten Quelltyp der SₓOperatoren in U:
    • Wenn S vorhanden ist und eine der Operatoren, die konvertiert werden U S, lautet dies Sₓ S.
    • Andernfalls ist einer der Operatoren, U die von Typen konvertiert werden, die umfassen E, der Sₓ 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 in U. Wenn genau ein umfassendster Typ nicht gefunden werden kann, ist die Konvertierung mehrdeutig, und ein Kompilierungszeitfehler tritt auf.
  • Suchen Sie den spezifischsten Zieltyp( Tₓ) der Operatoren in U:
    • Wenn einer der Operatoren, in die konvertiert TwirdU, lautet Tdies Tₓ .
    • Andernfalls ist einer der Operatoren, die in U Typen konvertiert werden, die von Tdiesen eingeschlossen sind, der Tₓ 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 in U. Wenn kein umfassendster Typ gefunden werden kann, ist die Konvertierung mehrdeutig, und ein Kompilierungszeitfehler tritt auf.
  • 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 "in Sₓ Tₓ" konvertiert wird.
    • Andernfalls ist die Konvertierung mehrdeutig und ein Kompilierungszeitfehler tritt auf.
  • Wenden Sie schließlich die Konvertierung an:
    • Wenn E der Typ Sₓnoch nicht vorhanden ist, wird eine explizite Standardkonvertierung von E in Sₓ ausgeführt.
    • Der spezifisch benutzerdefinierte Konvertierungsoperator wird aufgerufen, um von Sₓ zu Tₓkonvertieren.
    • Ist Tₓ dies nicht Tder Fehler, wird eine explizite Standardkonvertierung von "in Tₓ T " ausgeführt.

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 Tvorhanden 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? zu T?
  • Eine implizite oder explizite Konvertierung von S zu T?
  • Eine explizite Konvertierung von S? zu T.

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 ) ist false, ist das Ergebnis der Nullwert des Typs T?.
    • Andernfalls wird die Konvertierung als entwrapping von S? zu S, gefolgt von der zugrunde liegenden Konvertierung von S zu T, gefolgt von einem Umbruch von T zu .T?
  • Wenn die nullable Konvertierung von S zu T?, wird die Konvertierung als zugrunde liegende Konvertierung von S zu T gefolgt von einem Umbruch von T zu T?.
  • Wenn die NULL-Konvertierung von S? zu TNull erfolgt, wird die Konvertierung als Entwrapping von S? zu S gefolgt von der zugrunde liegenden Konvertierung von S zu T" ausgewertet.

10.6.2 Angehobene Konvertierungen

Bei einem benutzerdefinierten Konvertierungsoperator, der von einem nicht nullfähigen Werttyp in einen nicht nullablen Werttyp S Tkonvertiert 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 Sie F die gleiche Anzahl von Parametern.
  • Wenn F kein anonymous_function_signature enthalten ist, kann D es null oder mehr Parameter eines Typs geben, sofern kein Parameter eines D Ausgabeparameters ist.
  • Wenn F eine explizit eingegebene Parameterliste vorhanden ist, verfügt jeder Parameter in D denselben Modifizierern wie der entsprechende Parameter in F und eine Identitätskonvertierung ist zwischen dem entsprechenden Parameter in F.
  • 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 und D einen «TaskType» Rückgabetyp (§15.15.1) aufweist, wird jeder Parameter des F entsprechenden Parameters im DTextkörper F 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 und D einen «TaskType» Rückgabetyp aufweist, wird bei jedem Parameter F des entsprechenden Parameters im DTextkörper F ein gültiger Block (w.r.t §13.3) angegeben, in dem keine return Anweisung einen Ausdruck angibt.
  • Wenn der Textkörper F ein Ausdruck ist und entwederF nicht asynchron ist und D einen Nicht-Rückgabetypvoid aufweist oderF asynchron ist und D einen «TaskType»<T> Rückgabetyp T(§15.15.1) aufweist, ist der F DTextkörper F eines gültigen Ausdrucks (w.r.t §12), der implizit zu konvertierbar Tist.
  • Wenn der Textkörper F ein Block ist und entwederF nicht asynchron ist und D über einen nicht ungültigen Rückgabetyp Tverfügt oderF asynchron ist und D einen «TaskType»<T> Rückgabetyp aufweist, gibt jeder Parameter F den Typ des entsprechenden Parameters in D, ist der Textkörper F 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 konvertierbar Tist.

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 Typ A verwendet und einen Wert vom Typ Rzurü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 DelegattypFunc<int,int>, da bei x angabe des Typs intx + 1 ein gültiger Ausdruck ist, der implizit in typkonvertierbar intist.

Ebenso konvertiert die zweite Zuordnung die anonyme Funktion erfolgreich in den Stellvertretungstyp Func<int, doppelt> , da das Ergebnis von x + 1 (vom Typ int) implizit in Typ doublekonvertiert wird.

Die dritte Zuordnung ist jedoch ein Kompilierungszeitfehler, da das x Ergebnis des x + 1 Typs (des Typsdoubledouble) nicht implizit in typmäßig intkonvertierbar ist.

Die vierte Zuordnung konvertiert die anonyme asynchrone Funktion erfolgreich in den Delegattyp Func<int, Task<int>> , da das Ergebnis von x + 1 (vom Typ int) implizit in den effektiven Rückgabetyp int der asynchronen Lambda-Funktion konvertiert wird, die einen Rückgabetyp Task<int>aufweist.

Endbeispiel

Ein Lambda-Ausdruck F ist mit einem Ausdrucksstrukturtyp Expression<D> kompatibel, wenn F er mit dem Delegattyp Dkompatibel 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 Formulars E(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, outoder ref) des entsprechenden Parameters im parameter_list von D – mit Ausnahme von Parametern vom Typ dynamic, wobei der entsprechende Ausdruck den Typ object anstelle von dynamic.
    • 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
  • Eine Konvertierung gilt als vorhanden, wenn der Algorithmus von §12.8.10.2 eine einzige beste Methode M erzeugt, die kompatibel ist (§20.4) mit D.
  • Wenn es sich bei der ausgewählten Methode M um eine Instanzmethode handelt, bestimmt der instanzbezogene E 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 Methodengruppe F in einen Wert vom Typ D1.

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 Eoder ü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 ein System.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.