Dela via


10 konverteringar

10.1 Allmänt

En konvertering gör att ett uttryck konverteras till eller behandlas som av en viss typ. I det tidigare fallet kan en konvertering innebära en ändring i representationen. Konverteringar kan vara implicita eller explicita, och detta avgör om en explicit gjutning krävs.

Exempel: Till exempel är konverteringen från typ int till typ long implicit, så uttryck av typen int kan implicit behandlas som typ long. Den motsatta konverteringen, från typ long till typ int, är explicit och därför krävs en explicit gjutning.

int a = 123;
long b = a;      // implicit conversion from int to long
int c = (int) b; // explicit conversion from long to int

slutexempel

Vissa konverteringar definieras av språket. Program kan också definiera sina egna konverteringar (§10.5).

Vissa konverteringar på språket definieras från uttryck till typer, andra från typer till typer. En konvertering från en typ gäller för alla uttryck som har den typen.

Exempel:

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;

slutexempel

10.2 Implicita konverteringar

10.2.1 Allmänt

Följande konverteringar klassificeras som implicita konverteringar:

  • Identitetskonverteringar (§10.2.2)
  • Implicita numeriska konverteringar (§10.2.3)
  • Implicit uppräkningskonverteringar (§10.2.4)
  • Implicita interpolerade strängkonverteringar (§10.2.5)
  • Implicita referenskonverteringar (§10.2.8)
  • Boxningskonverteringar (§10.2.9)
  • Implicita dynamiska konverteringar (§10.2.10)
  • Implicit typparameterkonverteringar (§10.2.12)
  • Implicita konverteringar av konstanta uttryck (§10.2.11)
  • Användardefinierade (inklusive hävda) implicita konverteringar (§10.2.14)
  • Anonyma funktionskonverteringar (§10.2.15)
  • Metodgruppkonverteringar (§10.2.15)
  • Nullliterala konverteringar (§10.2.7)
  • Implicita nullbara konverteringar (§10.2.6)
  • Implicita tupppelkonverteringar (§10.2.13)
  • Standardliterala konverteringar (§10.2.16)
  • Implicita kastkonverteringar (§10.2.17)

Implicita konverteringar kan ske i en mängd olika situationer, inklusive funktionsmedlemsanrop (§12.6.6), gjutna uttryck (§12.9.7) och tilldelningar (§12.21).

De fördefinierade implicita konverteringarna lyckas alltid och orsakar aldrig undantag.

Obs! Korrekt utformade användardefinierade implicita konverteringar bör också uppvisa dessa egenskaper. slutkommentar

I konverteringssyfte är typerna object och dynamic identitetskonverterbara (§10.2.2).

Dynamiska konverteringar (§10.2.10) gäller dock endast för uttryck av typen dynamic (§8.2.4).

10.2.2 Identitetskonvertering

En identitetskonvertering konverterar från valfri typ till samma typ eller en typ som är likvärdig vid körning. En anledning till att konverteringen finns är att en typ T eller ett uttryck av typen T kan sägas vara konvertibel till T sig själv. Följande identitetskonverteringar finns:

  • Mellan T och T, för alla typer T.
  • Mellan T och T? för alla referenstyper T.
  • Mellan object och dynamic.
  • Mellan alla tuppelns typer med samma aritet och motsvarande konstruerade typ, när det finns en identitetskonvertering ValueTuple<...> mellan varje par av motsvarande elementtyper.
  • Mellan typer som konstruerats av samma generiska typ där det finns en identitetskonvertering mellan varje motsvarande typargument.

Exempel: Följande illustrerar den tredje regelns rekursiva karaktär:

(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;

Typerna av tupplar t1, t2 och t3 alla har två element: en int följt av en string. Tuppelns elementtyper kan själva utföras av tupplar, som i t4, t5och t6. Det finns en identitetskonvertering mellan varje par av motsvarande elementtyper, inklusive kapslade tupplar, och därför finns det en identitetskonvertering mellan typerna av tupplar t4, t5och t6.

slutexempel

Alla identitetskonverteringar är symmetriska. Om det finns en identitetskonvertering från T₁ till T₂finns det en identitetskonvertering från T₂ till T₁. Två typer är identitetskonverterbara när det finns en identitetskonvertering mellan två typer.

I de flesta fall har en identitetskonvertering ingen effekt vid körning. Men eftersom flyttalsoperationer kan utföras med högre precision än vad som föreskrivs i deras typ (§8.3.7), kan tilldelningen av deras resultat leda till en precisionsförlust och explicita avgjutningar garanteras minska precisionen till vad som föreskrivs av typen (§12.9.7).

10.2.3 Implicita numeriska konverteringar

De implicita numeriska konverteringarna är:

  • Från sbyte till short, int, long, float, doubleeller decimal.
  • Från byte till short, ushort, int, uint, long, ulong, float, eller doubledecimal.
  • Från short till int, long, float, doubleeller decimal.
  • Från ushort till int, uint, long, ulong, float, doubleeller decimal.
  • Från int till long, float, doubleeller decimal.
  • Från uint till long, ulong, float, doubleeller decimal.
  • Från long till float, doubleeller decimal.
  • Från ulong till float, doubleeller decimal.
  • Från char till ushort, int, uint, long, ulong, float, doubleeller decimal.
  • Från float till double.

Konverteringar från int, uint, long eller ulong till float och från long eller ulong till double kan orsaka en förlust av precision, men kommer aldrig att orsaka en förlust av omfattning. De andra implicita numeriska konverteringarna förlorar aldrig någon information.

Det finns inga fördefinierade implicita konverteringar till char typen, så värden för de andra integraltyperna konverteras inte automatiskt till char typen.

10.2.4 Implicit uppräkningskonverteringar

En implicit uppräkningskonvertering tillåter att en constant_expression (§12.23) med alla heltalstyper och värdet noll konverteras till alla enum_type och till alla nullable_value_type vars underliggande typ är en enum_type. I det senare fallet utvärderas konverteringen genom att konvertera till den underliggande enum_type och omsluta resultatet (§8.3.12).

10.2.5 Implicita interpolerade strängkonverteringar

En implicit interpolerad strängkonvertering tillåter att en interpolated_string_expression (§12.8.3) konverteras till System.IFormattable eller System.FormattableString (som implementerar System.IFormattable). När den här konverteringen tillämpas består inte ett strängvärde från den interpolerade strängen. I stället skapas en instans av System.FormattableString , enligt beskrivningen i §12.8.3.

10.2.6 Implicita nullbara konverteringar

De implicita nullbara konverteringarna är de nullbara konverteringar (§10.6.1) som härleds från implicita fördefinierade konverteringar.

10.2.7 Nullliterala konverteringar

Det finns en implicit konvertering från literalen null till en referenstyp eller nullbar värdetyp. Den här konverteringen ger en null-referens om måltypen är en referenstyp eller nullvärdet (§8.3.12) för den angivna nullbara värdetypen.

10.2.8 Implicita referenskonverteringar

De implicita referenskonverteringarna är:

  • Från alla reference_type till object och dynamic.
  • Från alla class_type S till alla class_type T , som tillhandahålls S härleds från T.
  • Från alla class_type S till alla interface_type T, tillhandahållna S implementerar T.
  • Från alla interface_type S till alla interface_type T , som tillhandahålls S härleds från T.
  • Från en array_type S med en elementtyp Sᵢ till en array_type T med en elementtyp Tᵢ, förutsatt att allt följande är sant:
    • S och T skiljer sig endast i elementtyp. Med andra ord och S T har samma antal dimensioner.
    • Det finns en implicit referenskonvertering från Sᵢ till Tᵢ.
  • Från en endimensionell matristyp S[] till System.Collections.Generic.IList<T>, System.Collections.Generic.IReadOnlyList<T>och deras basgränssnitt, förutsatt att det finns en implicit identitet eller referenskonvertering från S till T.
  • Från alla array_type till System.Array och de gränssnitt som implementeras.
  • Från alla delegate_type till System.Delegate och de gränssnitt som implementeras.
  • Från nullliteralen (§6.4.5.7) till alla referenstyper.
  • Från alla reference_type till en reference_type T om den har en implicit identitet eller referenskonvertering till en reference_type T₀ och T₀ har en identitetskonvertering till T.
  • Från alla reference_type till ett gränssnitt eller en ombudstyp T om den har en implicit identitet eller referenskonvertering till ett gränssnitt eller en ombudstyp T₀ och T₀ är varianskonverterad (§18.2.3.3) till T.
  • Implicita konverteringar som omfattar typparametrar som är kända för att vara referenstyper. Mer information om implicita konverteringar med typparametrar finns i §10.2.12 .

Implicita referenskonverteringar är de konverteringar mellan reference_typesom alltid kan bevisas lyckas och därför inte kräver några kontroller vid körning.

Referenskonverteringar, implicita eller explicita, ändrar aldrig referensidentiteten för objektet som konverteras.

Obs! Med andra ord, även om en referenskonvertering kan ändra referenstypen, ändras aldrig typen eller värdet för det objekt som refereras till. slutkommentar

10.2.9 Boxningskonverteringar

En boxningskonvertering tillåter att en value_type implicit konverteras till en reference_type. Följande boxningskonverteringar finns:

  • Från valfri value_type till typen object.
  • Från valfri value_type till typen System.ValueType.
  • Från valfri enum_type till typen System.Enum.
  • Från alla non_nullable_value_type till alla interface_type som implementeras av non_nullable_value_type.
  • Från alla non_nullable_value_type till alla interface_type I så att det finns en boxningskonvertering från non_nullable_value_type till en annan interface_type I₀ och I₀ har en identitetskonvertering till I.
  • Från alla non_nullable_value_type till alla interface_type I så att det finns en boxningskonvertering från non_nullable_value_type till en annan interface_type I₀ , och I₀ är varians-cabriolet (§18.2.3.3) till I.
  • Från alla nullable_value_type till alla reference_type där det finns en boxningskonvertering från den underliggande typen av nullable_value_type till reference_type.
  • Från en typparameter som inte är känd för att vara en referenstyp till någon typ så att konverteringen tillåts av §10.2.12.

Boxning av ett värde av typen icke-nullable-value-består av att allokera en objektinstans och kopiera värdet till den instansen.

Boxning av ett värde för en nullable_value_type ger en null-referens om det är null-värdet (HasValue är falskt), eller resultatet av att packa upp och boxa det underliggande värdet annars.

Obs! Boxningsprocessen kan föreställas när det gäller förekomsten av en boxningsklass för varje värdetyp. Överväg till exempel att implementera ett struct S gränssnitt I, med en boxningsklass med namnet 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();
    }
}

Boxning av ett värde v av typen S består nu av att köra uttrycket new S_Boxing(v) och returnera den resulterande instansen som ett värde för konverteringens måltyp. Det innebär att påståendena

S s = new S();
object box = s;

kan ses som liknar:

S s = new S();
object box = new S_Boxing(s);

Den inbillade boxningstypen som beskrivs ovan finns faktiskt inte. I stället har ett boxat värde av typen S körningstyp S, och en körningstypkontroll med operatorn is med en värdetyp som den högra operanden testar om den vänstra operanden är en boxad version av den högra operanden. Ett exempel:

int i = 123;
object box = i;
if (box is int)
{
    Console.Write("Box contains an int");
}

kommer att mata ut följande:

Box contains an int

En boxningskonvertering innebär att en kopia av värdet boxas. Detta skiljer sig från en konvertering av en reference_type för att skriva object, där värdet fortsätter att referera till samma instans och helt enkelt betraktas som den mindre härledda typen object. Följande är till exempel

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);
    }
}

matar ut värdet 10 i konsolen eftersom den implicita boxningsåtgärd som inträffar i tilldelningen av p till box gör att värdet p för kopieras. Hade Point deklarerats som en class i stället skulle värdet 20 vara utdata eftersom p och box refererar till samma instans.

Analogi av en boxning klass bör inte användas som mer än ett användbart verktyg för att föreställa sig hur boxning fungerar konceptuellt. Det finns många subtila skillnader mellan beteendet som beskrivs i den här specifikationen och det beteende som skulle bli resultatet av att boxning implementeras på just detta sätt.

slutkommentar

10.2.10 Implicita dynamiska konverteringar

Det finns en implicit dynamisk konvertering från ett uttryck av typen dynamisk till valfri typ T. Konverteringen är dynamiskt bunden §12.3.3, vilket innebär att en implicit konvertering söks vid körning från körningstypen för uttrycket till T. Om ingen konvertering hittas utlöses ett körningsfel.

Denna implicita konvertering strider till synes mot rådet i början av §10.2 att en implicit konvertering aldrig ska orsaka ett undantag. Det är dock inte själva konverteringen , utan resultatet av konverteringen som orsakar undantaget. Risken för körningsfel är en naturlig del av användningen av dynamisk bindning. Om dynamisk bindning av konverteringen inte önskas kan uttrycket först konverteras till objectoch sedan till önskad typ.

Exempel: Följande illustrerar implicita dynamiska konverteringar:

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

Tilldelningarna till s2 och i båda använder implicita dynamiska konverteringar, där bindningen av åtgärderna pausas fram till körning. Vid körning söks implicita konverteringar från körningstypen d(string) till måltypen. En konvertering hittas till string men inte till int.

slutexempel

10.2.11 Implicita konverteringar av konstanta uttryck

En implicit konvertering av konstanta uttryck tillåter följande konverteringar:

  • En constant_expression (§12.23) av typen int kan konverteras till typen sbyte, , shortbyte, ushort, uinteller ulong, förutsatt att värdet för constant_expression ligger inom måltypens intervall.
  • En constant_expression av typen long kan konverteras till typen ulong, förutsatt att värdet för constant_expression inte är negativt.

10.2.12 Implicita konverteringar med typparametrar

För en type_parameter T som är känd för att vara en referenstyp (§15.2.5) finns följande implicita referenskonverteringar (§10.2.8):

  • Från T till dess effektiva basklass C, från T till valfri basklass av C, och från T till alla gränssnitt som implementeras av C.
  • Från T till en interface_type I i Tden effektiva gränssnittsuppsättningen och från T till valfritt basgränssnitt för I.
  • Från T till en typparameter U som T är beroende av U (§15.2.5).

    Obs! Eftersom T är känt som en referenstyp är körningstypen U alltid en referenstyp inom omfånget Tför , även om U det inte är känt att den är en referenstyp vid kompileringstid. slutkommentar

  • Från nulllitralen (§6.4.5.7) till T.

För en type_parameter T som inte är känd för att vara en referenstyp §15.2.5 anses följande konverteringar som involverar T vara boxningskonverteringar (§10.2.9) vid kompileringstid. Vid körning, om T är en värdetyp, körs konverteringen som en boxningskonvertering. Vid körning, om T är en referenstyp, körs konverteringen som en implicit referenskonvertering eller identitetskonvertering.

  • Från T till dess effektiva basklass C, från T till valfri basklass av C, och från T till alla gränssnitt som implementeras av C.

    Obs! kommer att vara en av typerna System.Object, System.ValueTypeeller System.Enum (annars T skulle vara känd för att vara en referenstyp). C slutkommentar

  • Från T till en interface_type I i Tden effektiva gränssnittsuppsättningen och från T till valfritt basgränssnitt för I.

För en type_parameter T som inte är känd för att vara en referenstyp finns det en implicit konvertering från T till en typparameter U som anges T beror på U. Vid körning, om T är en värdetyp och U är en referenstyp, körs konverteringen som en boxningskonvertering. Vid körning, om både T och U är värdetyper, så T är och U nödvändigtvis samma typ och ingen konvertering utförs. Vid körning, om T är en referenstyp, är det U nödvändigtvis också en referenstyp och konverteringen körs som en implicit referenskonvertering eller identitetskonvertering (§15.2.5).

Följande ytterligare implicita konverteringar finns för en viss typparameter T:

  • Från T till en referenstyp S om den har en implicit konvertering till en referenstyp S₀ och S₀ har en identitetskonvertering till S. Vid körning körs konverteringen på samma sätt som konverteringen till S₀.
  • Från T till en gränssnittstyp I om den har en implicit konvertering till en gränssnittstyp I₀och I₀ är varianskonverterad till I (§18.2.3.3). Vid körning, om T är en värdetyp, körs konverteringen som en boxningskonvertering. Annars körs konverteringen som en implicit referenskonvertering eller identitetskonvertering.

I samtliga fall säkerställer reglerna att en konvertering körs som en boxningskonvertering om och endast om konverteringen vid körningen är från en värdetyp till en referenstyp.

10.2.13 Implicita tupppelkonverteringar

Det finns en implicit konvertering från ett tupppeluttryck E till en tuppelns typ T om E har samma aritet som T och en implicit konvertering finns från varje element i E till motsvarande elementtyp i T. Konverteringen utförs genom att skapa en instans av Tmotsvarande System.ValueTuple<...> typ och initiera vart och ett av dess fält i ordning från vänster till höger genom att utvärdera motsvarande tuppelns elementuttryck E, konvertera det till motsvarande elementtyp med hjälp av T den implicita konverteringen som hittades och initiera fältet med resultatet.

Om ett elementnamn i tuppelns uttryck inte matchar ett motsvarande elementnamn i tuppelns typ, ska en varning utfärdas.

Exempel:

(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

Deklarationerna för t1, t2t4 och t5 är alla giltiga, eftersom implicita konverteringar finns från elementuttrycken till motsvarande elementtyper. Deklarationen av t3 är ogiltig eftersom det inte finns någon konvertering från null till int. Deklarationen av t5 orsakar en varning eftersom elementnamnen i tuppelns uttryck skiljer sig från dem i tuppelns typ.

slutexempel

10.2.14 Användardefinierade implicita konverteringar

En användardefinierad implicit konvertering består av en valfri implicit standardkonvertering följt av körning av en användardefinierad implicit konverteringsoperator följt av en annan valfri implicit standardkonvertering. De exakta reglerna för utvärdering av användardefinierade implicita konverteringar beskrivs i §10.5.4.

10.2.15 Anonyma funktionskonverteringar och metodgruppkonverteringar

Anonyma funktioner och metodgrupper har inte typer i sig själva, men de kan implicit konverteras till delegerade typer. Dessutom kan vissa lambda-uttryck implicit konverteras till uttrycksträdstyper. Anonyma funktionskonverteringar beskrivs mer detaljerat i §10.7 och metodgruppkonverteringar i §10.8.

10.2.16 Standardliterala konverteringar

Det finns en implicit konvertering från en default_literal (§12.8.21) till vilken typ som helst. Den här konverteringen genererar standardvärdet (§9.3) av den härledda typen.

10.2.17 Implicita kastkonverteringar

Även om utkastsuttryck inte har någon typ kan de implicit konverteras till valfri typ.

10.3 Explicita konverteringar

10.3.1 Allmänt

Följande konverteringar klassificeras som explicita konverteringar:

  • Alla implicita konverteringar (§10.2)
  • Explicita numeriska konverteringar (§10.3.2)
  • Explicita uppräkningskonverteringar (§10.3.3)
  • Explicita nullbara konverteringar (§10.3.4)
  • Explicita tupppelkonverteringar (§10.3.6)
  • Explicita referenskonverteringar (§10.3.5)
  • Explicita gränssnittskonverteringar
  • Avboxning av konverteringar (§10.3.7)
  • Explicita typparameterkonverteringar (§10.3.8)
  • Användardefinierade explicita konverteringar (§10.3.9)

Explicita konverteringar kan ske i gjutna uttryck (§12.9.7).

Uppsättningen explicita konverteringar innehåller alla implicita konverteringar.

Obs! Detta gör till exempel att en explicit gjutning kan användas när det finns en implicit identitetskonvertering för att tvinga valet av en viss metodöverbelastning. slutkommentar

Explicita konverteringar som inte är implicita konverteringar är konverteringar som inte alltid kan bevisas lyckas, konverteringar som är kända för att förlora information och konverteringar mellan domäner av typer som är tillräckligt olika för att förtjäna explicit notation.

10.3.2 Explicita numeriska konverteringar

De explicita numeriska konverteringarna är konverteringarna från en numeric_type till en annan numeric_type för vilka en implicit numerisk konvertering (§10.2.3) inte redan finns:

  • Från sbyte till byte, ushort, uint, ulongeller char.
  • Från byte till sbyte eller char.
  • Från short till sbyte, byte, ushort, uint, ulongeller char.
  • Från ushort till sbyte, byte, shorteller char.
  • Från int till sbyte, byte, short, ushort, uint, ulongeller char.
  • Från uint till sbyte, byte, short, ushort, inteller char.
  • Från long till sbyte, byte, short, ushort, int, uint, ulongeller char.
  • Från ulong till sbyte, byte, short, ushort, int, uint, longeller char.
  • Från char till sbyte, byteeller short.
  • Från float till sbyte, byte, short, ushort, int, uint, long, ulong, , chareller decimal.
  • Från double till sbyte, byte, short, ushort, int, uint, long, ulong, char, , floateller decimal.
  • Från decimal till sbyte, byte, short, ushort, int, uint, long, ulong, char, , floateller double.

Eftersom explicita konverteringar omfattar alla implicita och explicita numeriska konverteringar är det alltid möjligt att konvertera från alla numeric_type till andra numeric_type med ett gjutet uttryck (§12.9.7).

De explicita numeriska konverteringarna kan förlora information eller eventuellt orsaka att undantag utlöses. En explicit numerisk konvertering bearbetas på följande sätt:

  • För en konvertering från en integrerad typ till en annan integrerad typ beror bearbetningen på överflödets kontrollkontext (§12.8.20) där konverteringen äger rum:
    • I ett checked sammanhang lyckas konverteringen om värdet för källoperatorn ligger inom måltypens intervall, men genererar ett System.OverflowException om värdet för källoperatorn ligger utanför måltypens intervall.
    • I ett unchecked sammanhang lyckas konverteringen alltid och fortsätter på följande sätt.
      • Om källtypen är större än måltypen trunkeras källvärdet genom att dess "extra" viktigaste bitar ignoreras. Resultatet behandlas sedan som ett värde av måltypen.
      • Om källtypen har samma storlek som måltypen behandlas källvärdet som ett värde av måltypen
  • För en konvertering från decimal till en integrerad typ avrundas källvärdet mot noll till närmaste integralvärde, och det här integralvärdet blir resultatet av konverteringen. Om det resulterande integralvärdet ligger utanför måltypens intervall genereras ett System.OverflowException .
  • För en konvertering från float eller double till en integrerad typ beror bearbetningen på överflödeskontrollkontexten (§12.8.20) där konverteringen äger rum:
    • I en kontrollerad kontext fortsätter konverteringen enligt följande:
      • Om operandvärdet är NaN eller oändligt genereras en System.OverflowException .
      • Annars avrundas källoperatorn mot noll till närmaste integralvärde. Om det här integralvärdet ligger inom måltypens intervall är det här värdet resultatet av konverteringen.
      • Annars kastas en System.OverflowException .
    • I en omarkerad kontext lyckas konverteringen alltid och fortsätter enligt följande.
      • Om värdet för operanden är NaN eller oändligt är resultatet av konverteringen ett ospecificerat värde av måltypen.
      • Annars avrundas källoperatorn mot noll till närmaste integralvärde. Om det här integralvärdet ligger inom måltypens intervall är det här värdet resultatet av konverteringen.
      • Annars är resultatet av konverteringen ett ospecificerat värde för måltypen.
  • För en konvertering från double till floatdouble avrundas värdet till närmaste float värde. Om värdet double är för litet för att representeras som ett floatblir resultatet noll med samma tecken som värdet. Om värdets double storlek är för stor för att representeras som en floatblir resultatet oändligt med samma tecken som värdet. Om värdet double är NaN blir resultatet även NaN.
  • För en konvertering från float eller double till decimalkonverteras källvärdet till decimal representation och avrundas till närmaste tal vid behov (§8.3.8).
    • Om källvärdet är för litet för att representeras som ett decimalblir resultatet noll, vilket bevarar tecknet för det ursprungliga värdet om decimal det stöder signerade nollvärden.
    • Om källvärdets storlek är för stor för att representeras som ett decimal, eller om värdet är oändligt, är resultatet oändligheten som bevarar tecknet för det ursprungliga värdet, om decimalrepresentationen stöder infiniteter, annars genereras en System.OverflowException.
    • Om källvärdet är NaN blir resultatet NaN om decimalrepresentationen stöder NaN. annars genereras en System.OverflowException.
  • För en konvertering från decimal till float eller doubledecimal avrundas värdet till närmaste double värde eller float värde. Om källvärdets storlek är för stor för att representeras i måltypen, eller om värdet är oändligt, blir resultatet oändligheten som bevarar tecknet för det ursprungliga värdet. Om källvärdet är NaN blir resultatet NaN. Även om den här konverteringen kan förlora precisionen orsakar den aldrig ett undantagsfel.

Obs! Typen decimal krävs inte för att stödja infiniteter eller NaN-värden, men kan göra det. Dess intervall kan vara mindre än intervallet float för och double, men är inte garanterat. För decimal representationer utan infiniteter eller NaN-värden, och med ett intervall som är mindre än float, blir resultatet av en konvertering från decimal till antingen float eller double aldrig oändlighet eller NaN. slutkommentar

10.3.3 Explicit uppräkningskonverteringar

De explicita uppräkningskonverteringarna är:

  • Från sbyte, byte, short, ushort, int, uint, long, ulong, char, float, , doubleeller decimal till någon enum_type.
  • Från alla enum_type till sbyte, byte, shortushort, , int, uint, long, ulong, , char, float, , doubleeller decimal.
  • Från alla enum_type till andra enum_type.

En explicit uppräkningskonvertering mellan två typer bearbetas genom att alla deltagande enum_type behandlas som den underliggande typen av enum_type och sedan utföra en implicit eller explicit numerisk konvertering mellan de resulterande typerna.

Exempel: Med en enum_type E med och underliggande typ av intbearbetas en konvertering från E till byte som en explicit numerisk konvertering (§10.3.2) från int till byteoch en konvertering från byte till E bearbetas som en implicit numerisk konvertering (§10.2.3) från byte till int. slutexempel

10.3.4 Explicita nullbara konverteringar

De explicita nullbara konverteringarna är de nullbara konverteringar (§10.6.1) som härleds från explicita och implicita fördefinierade konverteringar.

10.3.5 Explicita referenskonverteringar

De explicita referenskonverteringarna är:

  • Från objekt till andra reference_type.
  • Från alla class_type S till alla class_type T , som tillhandahålls S är en basklass för T.
  • Från alla class_type S till alla interface_type T, tillhandahålls S inte är förseglad och tillhandahålls S inte implementerar T.
  • Från alla interface_type S till alla class_type T, tillhandahålls T inte är förseglade eller tillhandahållna T Sredskap .
  • Från alla interface_typeS till alla interface_type T , som tillhandahålls S härleds inte från T.
  • Från en array_type S med en elementtyp Sᵢ till en array_type T med en elementtyp Tᵢ, förutsatt att allt följande är sant:
    • S och T skiljer sig endast i elementtyp. Med andra ord och S T har samma antal dimensioner.
    • Det finns en explicit referenskonvertering från Sᵢ till Tᵢ.
  • Från System.Array och de gränssnitt som implementeras, till alla array_type.
  • Från en endimensionell array_type S[] till , och dess basgränssnitt, förutsatt att det finns en identitetskonvertering eller explicit referenskonvertering från S till T. System.Collections.Generic.IReadOnlyList<T>System.Collections.Generic.IList<T>
  • Från System.Collections.Generic.IList<S>, System.Collections.Generic.IReadOnlyList<S>och deras basgränssnitt till en endimensionell matristyp T[], förutsatt att det finns en identitetskonvertering eller explicit referenskonvertering från S till T.
  • Från System.Delegate och de gränssnitt som implementeras till alla delegate_type.
  • Från en referenstyp S till en referenstyp T om den har en explicit referenskonvertering från S till en referenstyp T₀ och T₀ det finns en identitetskonvertering från T₀ till T.
  • Från en referenstyp S till ett gränssnitt eller en ombudstyp T om det finns en explicit referenskonvertering från S till ett gränssnitt eller en ombudstyp T₀ och antingen T₀ är varianskonverterad till T eller T är varianskonverterad till T₀ §18.2.3.3.
  • Från D<S₁...Sᵥ> till D<T₁...Tᵥ> där D<X₁...Xᵥ> är en allmän ombudstyp, D<S₁...Sᵥ> är inte kompatibel med eller identisk D<T₁...Tᵥ>med , och för varje typparameter Xᵢ av D följande undantag:
    • Om Xᵢ är invariant är det Sᵢ identiskt med Tᵢ.
    • Om Xᵢ är covariant finns det en identitetskonvertering, implicit referenskonvertering eller explicit referenskonvertering från Sᵢ till Tᵢ.
    • Om Xᵢ är kontravariant Sᵢ är de Tᵢ antingen identiska eller båda referenstyperna.
  • Explicita konverteringar med typparametrar som är kända för att vara referenstyper. Mer information om explicita konverteringar med typparametrar finns i §10.3.8.

De explicita referenskonverteringarna är de konverteringar mellan reference_typesom kräver körningskontroller för att säkerställa att de är korrekta.

För att en explicit referenskonvertering ska lyckas vid körning ska värdet för källoperatorn vara null, eller så ska typen av objektet som refereras av källoperatorn vara en typ som kan konverteras till måltypen genom en implicit referenskonvertering (§10.2.8). Om en explicit referenskonvertering misslyckas genereras en System.InvalidCastException .

Obs! Referenskonverteringar, implicita eller explicita, ändrar aldrig själva referensvärdet (§8.2.1), endast dess typ. Det ändrar inte heller typen eller värdet för det objekt som refereras till. slutkommentar

10.3.6 Explicita tupplar

Det finns en explicit konvertering från ett tupppeluttryck E till en tuppelns typ T om E det finns samma aritet som T och det finns en implicit eller explicit konvertering från varje element i E till motsvarande elementtyp i T. Konverteringen utförs genom att skapa en instans av Tmotsvarande System.ValueTuple<...> typ och initiera vart och ett av dess fält i ordning från vänster till höger genom att utvärdera motsvarande tuppelns elementuttryck E, konvertera det till motsvarande elementtyp med hjälp av T den explicita konverteringen som hittades och initiera fältet med resultatet.

10.3.7 Avboxningskonverteringar

En konvertering med avboxning gör att en reference_type uttryckligen kan konverteras till en value_type. Följande konverteringar för avboxning finns:

  • Från typen object till valfri value_type.
  • Från typen System.ValueType till valfri value_type.
  • Från typen System.Enum till valfri enum_type.
  • Från alla interface_type till alla non_nullable_value_type som implementerar interface_type.
  • Från alla interface_type I till alla non_nullable_value_type där det finns en avboxningskonvertering från en interface_type I₀ till non_nullable_value-typen och en identitetskonvertering från I till I₀.
  • Från alla interface_type I till alla non_nullable_value_type där det sker en avboxningskonvertering från en interface_type I₀ till non_nullable_value_type och antingen I₀ är variance_convertible till I eller I är varianskonverterad till I₀ (§18.2.3.3).
  • Från alla reference_type till alla nullable_value_type där det finns en avboxningskonvertering från reference_type till nullable_value_type underliggande non_nullable_value_type.
  • Från en typparameter som inte är känd för att vara en värdetyp till någon typ så att konverteringen tillåts av §10.3.8.

En avboxningsåtgärd till en non_nullable_value_type består av att först kontrollera att objektinstansen är ett boxat värde för den angivna non_nullable_value_type och sedan kopiera värdet från instansen.

Om du avboxar till en nullable_value_type genereras värdet null för nullable_value_type om källoperatorn är null, eller det omslutna resultatet av att ta bort instansen av objektet till den underliggande typen av nullable_value_type annat.

Obs! Med hänvisning till den imaginära boxningsklassen som beskrivs i §10.2.9 består en konvertering av en objektruta till en value_type S av att köra uttrycket ((S_Boxing)box).value. Det innebär att påståendena

object box = new S();
S s = (S)box;

motsvarar konceptuellt

object box = new S_Boxing(new S());
S s = ((S_Boxing)box).value;

slutkommentar

För en avboxningskonvertering till en viss non_nullable_value_type ska lyckas vid körning ska värdet för källoperatorn vara en referens till ett boxat värde för den non_nullable_value_type. Om källoperatorn är null en System.NullReferenceException utlöses. Om källoperatorn är en referens till ett inkompatibelt objekt genereras en System.InvalidCastException .

För en avboxningskonvertering till en viss nullable_value_type ska lyckas vid körningen ska värdet för källoperatorn vara antingen null eller en referens till ett boxat värde för den underliggande non_nullable_value_type för nullable_value_type. Om källoperatorn är en referens till ett inkompatibelt objekt genereras en System.InvalidCastException .

10.3.8 Explicita konverteringar med typparametrar

För en type_parameter T som är känd för att vara en referenstyp (§15.2.5) finns följande explicita referenskonverteringar (§10.3.5):

  • Från den effektiva basklassen C T för till T och från valfri basklass C till T.
  • Från alla interface_type till T.
  • Från T till alla interface_type I förutsatt att det inte redan finns en implicit referenskonvertering från T till I.
  • Från en type_parameter U till T som T är beroende av U (§15.2.5).

    Obs! Eftersom T är känd för att vara en referenstyp, inom omfånget Tför , kommer körningstypen för dig alltid att vara en referenstyp, även om U det inte är känt att vara en referenstyp vid kompileringstid. slutkommentar

För en type_parameter T som inte är känd för att vara en referenstyp (§15.2.5), anses följande konverteringar som involverar T vara unboxing-konverteringar (§10.3.7) vid kompileringstid. Vid körning, om T är en värdetyp, körs konverteringen som en konvertering som avboxas. Vid körning, om T är en referenstyp, körs konverteringen som en explicit referenskonvertering eller identitetskonvertering.

  • Från den effektiva basklassen C T för till T och från valfri basklass C till T.

    Obs! C är en av typerna System.Object, System.ValueTypeeller System.Enum (annars T är det känt att det är en referenstyp). slutkommentar

  • Från alla interface_type till T.

För en type_parameter T som inte är känd för att vara en referenstyp (§15.2.5) finns följande explicita konverteringar:

  • Från T till alla interface_type I förutsatt att det inte redan finns en implicit konvertering från T till I. Denna konvertering består av en implicit boxningskonvertering (§10.2.9) från T till object följt av en explicit referenskonvertering från object till I. Vid körning, om T är en värdetyp, körs konverteringen som en boxningskonvertering följt av en explicit referenskonvertering. Vid körning, om T är en referenstyp, körs konverteringen som en explicit referenskonvertering.
  • Från en typparameter U till angivet som T är U beroende av (§15.2.5T). Vid körning, om T är en värdetyp och U är en referenstyp, körs konverteringen som en konvertering som avboxas. Vid körning, om både T och U är värdetyper, så T är och U nödvändigtvis samma typ och ingen konvertering utförs. Vid körning, om T är en referenstyp, är det U nödvändigtvis också en referenstyp och konverteringen körs som en explicit referenskonvertering eller identitetskonvertering.

I samtliga fall säkerställer reglerna att en konvertering körs som en konvertering utan inkorg om och endast om konverteringen vid körningen är från en referenstyp till en värdetyp.

Ovanstående regler tillåter inte en direkt explicit konvertering från en icke-tränad typparameter till en icke-gränssnittstyp, vilket kan vara överraskande. Anledningen till denna regel är att förhindra förvirring och göra semantiken för sådana konverteringar tydliga.

Exempel: Överväg följande deklaration:

class X<T>
{
    public static long F(T t)
    {
        return (long)t;         // Error
    }
}

Om den direkta explicita konverteringen av t till long var tillåten kan man enkelt förvänta sig att det X<int>.F(7) skulle returnera 7L. Det skulle det dock inte göra, eftersom de numeriska standardkonverteringarna endast beaktas när typerna är kända för att vara numeriska vid bindningstid. För att semantiken ska bli tydlig måste exemplet ovan i stället skrivas:

class X<T>
{
    public static long F(T t)
    {
        return (long)(object)t;         // Ok, but will only work when T is long
    }
}

Den här koden kompileras nu, men körningen X<int>.F(7) utlöser sedan ett undantag vid körning, eftersom en ruta int inte kan konverteras direkt till en long.

slutexempel

10.3.9 Användardefinierade explicita konverteringar

En användardefinierad explicit konvertering består av en valfri explicit standardkonvertering följt av körning av en användardefinierad implicit eller explicit konverteringsoperator följt av en annan valfri explicit standardkonvertering. De exakta reglerna för utvärdering av användardefinierade explicita konverteringar beskrivs i §10.5.5.

10.4 Standardkonverteringar

10.4.1 Allmänt

Standardkonverteringarna är de fördefinierade konverteringar som kan ske som en del av en användardefinierad konvertering.

10.4.2 Implicita standardkonverteringar

Följande implicita konverteringar klassificeras som implicita standardkonverteringar:

  • Identitetskonverteringar (§10.2.2)
  • Implicita numeriska konverteringar (§10.2.3)
  • Implicita nullbara konverteringar (§10.2.6)
  • Nullliterala konverteringar (§10.2.7)
  • Implicita referenskonverteringar (§10.2.8)
  • Boxningskonverteringar (§10.2.9)
  • Implicita konverteringar av konstanta uttryck (§10.2.11)
  • Implicita konverteringar med typparametrar (§10.2.12)

Implicita standardkonverteringar exkluderar specifikt användardefinierade implicita konverteringar.

10.4.3 Explicita standardkonverteringar

De explicita standardkonverteringarna är alla implicita standardkonverteringar plus delmängden av de explicita konverteringar för vilka det finns en implicit omvänd standardkonvertering.

Obs! Om det med andra ord finns en implicit standardkonvertering från en typ A till en typ B, så finns det en explicit standardkonvertering från typ A till typ B och från typ B till typ A. slutkommentar

10.5 Användardefinierade konverteringar

10.5.1 Allmänt

Med C# kan de fördefinierade implicita och explicita konverteringarna utökas med användardefinierade konverteringar. Användardefinierade konverteringar introduceras genom att konverteringsoperatorer (§15.10.4) deklareras i klass- och structtyper.

10.5.2 Tillåtna användardefinierade konverteringar

C# tillåter endast att vissa användardefinierade konverteringar deklareras. I synnerhet går det inte att omdefiniera en redan befintlig implicit eller explicit konvertering.

För en viss källtyp S och måltyp T, om S eller T är nullbara värdetyper, låter S₀ och T₀ refererar du till deras underliggande typer, i annat fall S₀ och T₀ är lika S med respektive T . En klass eller struct tillåts deklarera en konvertering från en källtyp S till en måltyp T endast om allt av följande är sant:

  • S₀ och T₀ är olika typer.
  • Antingen S₀ eller T₀ är den klass- eller structtyp där operatordeklarationen äger rum.
  • Varken eller S₀ T₀ är en interface_type.
  • Förutom användardefinierade konverteringar finns det ingen konvertering från S till T eller från T till S.

De begränsningar som gäller för användardefinierade konverteringar anges i §15.10.4.

10.5.3 Utvärdering av användardefinierade konverteringar

En användardefinierad konvertering konverterar ett källuttryck, som kan ha en källtyp, till en annan typ som kallas måltyp. Utvärdering av ett användardefinierat konverteringscenter för att hitta den mest specifika användardefinierade konverteringsoperatorn för källuttrycket och måltypen. Den här bestämningen är uppdelad i flera steg:

  • Hitta den uppsättning klasser och structs som användardefinierade konverteringsoperatorer kommer att övervägas från. Den här uppsättningen består av källtypen och dess basklasser, om källtypen finns, tillsammans med måltypen och dess basklasser. För detta ändamål förutsätts det att endast klasser och structs kan deklarera användardefinierade operatorer och att icke-klasstyper inte har några basklasser. Om antingen käll- eller måltypen är en nullable-value-type används deras underliggande typ i stället.
  • Från den uppsättningen av typer avgör du vilka användardefinierade och hävda konverteringsoperatorer som är tillämpliga. För att en konverteringsoperatör ska vara tillämplig ska det vara möjligt att utföra en standardkonvertering (§10.4) från källuttrycket till operandtypen för operatören, och det ska vara möjligt att utföra en standardkonvertering från operatörens resultattyp till måltypen.
  • Från uppsättningen med tillämpliga användardefinierade operatorer avgör du vilken operator som är tydligast den mest specifika. I allmänna termer är den mest specifika operatorn operatorn vars operandtyp är "närmast" källuttrycket och vars resultattyp är "närmast" måltypen. Användardefinierade konverteringsoperatorer föredras framför hävda konverteringsoperatorer. De exakta reglerna för att upprätta den mest specifika användardefinierade konverteringsoperatorn definieras i följande underfunktioner.

När en mest specifik användardefinierad konverteringsoperator har identifierats innebär den faktiska körningen av den användardefinierade konverteringen upp till tre steg:

  • Om det behövs utför du först en standardkonvertering från källuttrycket till operandtypen för den användardefinierade eller hävda konverteringsoperatorn.
  • Anropa sedan den användardefinierade eller hävda konverteringsoperatorn för att utföra konverteringen.
  • Om det behövs utför du slutligen en standardkonvertering från resultattypen för den användardefinierade konverteringsoperatorn till måltypen.

Utvärdering av en användardefinierad konvertering omfattar aldrig mer än en användardefinierad eller hävd konverteringsoperator. Med andra ord kommer en konvertering från typ S till typ T aldrig först att köra en användardefinierad konvertering från S till X och sedan köra en användardefinierad konvertering från X till T.

  • Exakta definitioner av utvärdering av användardefinierade implicita eller explicita konverteringar anges i följande underkliender. Definitionerna använder följande termer:
  • Om det finns en implicit standardkonvertering (§10.4.2) från en typ A till en typ B, och om varken A eller B interface_type s, A så sägs den omfattas av B, och B sägs omfatta . A
  • Om det finns en implicit standardkonvertering (§10.4.2) från ett uttryck E till en typ B, och om varken B eller typen av E (om den har en) är interface_type s , sägs den E omfattas avB och B sägs omfattaE.
  • Den mest omfattande typen i en uppsättning typer är den typ som omfattar alla andra typer i uppsättningen. Om ingen enskild typ omfattar alla andra typer har uppsättningen ingen mest omfattande typ. I mer intuitiva termer är den mest omfattande typen den "största" typen i uppsättningen – den typ som var och en av de andra typerna implicit kan konverteras till.
  • Den mest omfattande typen i en uppsättning typer är den typ som omfattas av alla andra typer i uppsättningen. Om ingen enskild typ omfattas av alla andra typer har uppsättningen ingen mest omfattande typ. I mer intuitiva termer är den mest omfattande typen den "minsta" typen i uppsättningen – den typ som implicit kan konverteras till var och en av de andra typerna.

10.5.4 Användardefinierade implicita konverteringar

En användardefinierad implicit konvertering från ett uttryck E till en typ T bearbetas på följande sätt:

  • Fastställa typerna S, S₀ och T₀.

    • Om E har en typ, låt vara S den typen.
    • Om S eller T är nullbara värdetyper, låt Sᵢ och Tᵢ vara deras underliggande typer, annars låt Sᵢ och Tᵢ vara S och T, respektive.
    • Om Sᵢ eller Tᵢ är typparametrar, låt S₀ och T₀ vara deras effektiva basklasser, annars låt S₀ och T₀ vara Sₓ och Tᵢ, respektive.
  • Leta reda på vilken uppsättning typer, D, som användardefinierade konverteringsoperatorer ska övervägas från. Den här uppsättningen består av S₀ (om S₀ finns och är en klass eller struct), basklasserna S₀ för (om S₀ finns och är en klass) och T₀ (om T₀ är en klass eller struct). En typ läggs bara till i uppsättningen D om en identitetskonvertering till en annan typ som redan ingår i uppsättningen inte finns.

  • Hitta uppsättningen med tillämpliga användardefinierade och hävda konverteringsoperatorer, U. Den här uppsättningen består av de användardefinierade och lyfte implicita konverteringsoperatorerna som deklarerats av klasserna eller structarna i D som konverterar från en typ som omfattar E till en typ som omfattas av T. Om U är tom är konverteringen odefinierad och ett kompileringsfel inträffar.

    • Om S finns och någon av operatorerna i U konvertera från Sär S.Sₓ
    • Annars Sₓ är den mest omfattande typen i den kombinerade uppsättningen av källtyper för operatorerna i U. Om exakt en mest omfattande typ inte kan hittas är konverteringen tvetydig och ett kompileringsfel inträffar.
  • Hitta den mest specifika måltypen, Tₓ, för operatorerna i U:

    • Om någon av operatorerna konverteras U till Tär T.Tₓ
    • Annars Tₓ är den mest omfattande typen i den kombinerade uppsättningen av måltyper för operatorerna i U. Om exakt en mest omfattande typ inte kan hittas är konverteringen tvetydig och ett kompileringsfel inträffar.
  • Hitta den mest specifika konverteringsoperatorn:

    • Om U innehåller exakt en användardefinierad konverteringsoperator som konverterar från Sₓ till Tₓär detta den mest specifika konverteringsoperatorn.
    • Om U innehåller exakt en hävd konverteringsoperator som konverterar från Sₓ till Tₓär detta annars den mest specifika konverteringsoperatorn.
    • Annars är konverteringen tvetydig och ett kompileringsfel inträffar.
  • Tillämpa slutligen konverteringen:

    • Om E inte redan har typen Sₓutförs en implicit standardkonvertering från E till Sₓ .
    • Den mest specifika konverteringsoperatorn anropas för att konvertera från Sₓ till Tₓ.
    • Om Tₓ inte Tutförs en implicit standardkonvertering från Tₓ till T .

Det finns en användardefinierad implicit konvertering från en typ S till en typ T om det finns en användardefinierad implicit konvertering från en variabel av typen S till T.

10.5.5 Användardefinierade explicita konverteringar

En användardefinierad explicit konvertering från ett uttryck E till en typ T bearbetas på följande sätt:

  • Fastställa typerna S, S₀ och T₀.
    • Om E har en typ, låt vara S den typen.
    • Om S eller T är nullbara värdetyper, låt Sᵢ och Tᵢ vara deras underliggande typer, annars låt Sᵢ och Tᵢ vara S och T, respektive.
    • Om Sᵢ eller Tᵢ är typparametrar, låt S₀ och T₀ vara deras effektiva basklasser, annars låt S₀ och T₀ vara Sᵢ och Tᵢ, respektive.
  • Leta reda på vilken uppsättning typer, D, som användardefinierade konverteringsoperatorer ska övervägas från. Den här uppsättningen består av (om S₀ finns och är en klass eller struct), basklasserna S₀ för (om S₀ finns och är en klass), T₀ (om T₀ är en klass eller struct) och basklasserna T₀ för (om T₀ är en S₀ klass). A -typen läggs endast till i uppsättningen D om en identitetskonvertering till en annan typ som redan ingår i uppsättningen inte finns.
  • Hitta uppsättningen med tillämpliga användardefinierade och hävda konverteringsoperatorer, U. Den här uppsättningen består av de användardefinierade och lyfta implicita eller explicita konverteringsoperatorerna som deklarerats av klasserna eller structarna i D som konverterar från en typ som omfattar E eller omfattas av S (om den finns) till en typ som omfattar eller omfattas av T. Om U är tom är konverteringen odefinierad och ett kompileringsfel inträffar.
  • Hitta den mest specifika källtypen, Sₓ, för operatorerna i U:
    • Om S finns och någon av operatorerna i U konvertera från Sär S.Sₓ
    • Om någon av operatorerna i U konverterar från typer som omfattar Eär annars Sₓ den mest omfattande typen i den kombinerade uppsättningen av källtyper för dessa operatorer. Om ingen mest omfattande typ kan hittas är konverteringen tvetydig och ett kompileringsfel inträffar.
    • Annars Sₓ är den mest omfattande typen i den kombinerade uppsättningen av källtyper för operatorerna i U. Om exakt en mest omfattande typ inte kan hittas är konverteringen tvetydig och ett kompileringsfel inträffar.
  • Hitta den mest specifika måltypen, Tₓ, för operatorerna i U:
    • Om någon av operatorerna konverteras U till Tär T.Tₓ
    • Om någon av operatorerna i U konverterar till typer som omfattas av Tär annars Tₓ den mest omfattande typen i den kombinerade uppsättningen av måltyper för dessa operatorer. Om exakt en mest omfattande typ inte kan hittas är konverteringen tvetydig och ett kompileringsfel inträffar.
    • Annars Tₓ är den mest omfattande typen i den kombinerade uppsättningen av måltyper för operatorerna i U. Om ingen mest omfattande typ kan hittas är konverteringen tvetydig och ett kompileringsfel inträffar.
  • Hitta den mest specifika konverteringsoperatorn:
    • Om U innehåller exakt en användardefinierad konverteringsoperator som konverterar från Sₓ till Tₓär detta den mest specifika konverteringsoperatorn.
    • Om U innehåller exakt en hävd konverteringsoperator som konverterar från Sₓ till Tₓär detta annars den mest specifika konverteringsoperatorn.
    • Annars är konverteringen tvetydig och ett kompileringsfel inträffar.
  • Tillämpa slutligen konverteringen:
    • Om E inte redan har typen Sₓutförs en explicit standardkonvertering från E till Sₓ .
    • Den mest specifika användardefinierade konverteringsoperatorn anropas för att konvertera från Sₓ till Tₓ.
    • Om Tₓ inte Tutförs en explicit standardkonvertering från Tₓ till T .

Det finns en användardefinierad explicit konvertering från en typ S till en typ T om det finns en användardefinierad explicit konvertering från en variabel av typen S till T.

10.6 Konverteringar som omfattar nullbara typer

10.6.1 Nullable Conversions

Med null-konverteringar kan fördefinierade konverteringar som körs på icke-nullbara värdetyper också användas med nullbara former av dessa typer. För var och en av de fördefinierade implicita eller explicita konverteringarna som konverterar från en värdetyp S som inte kan nollställas till en värdetyp T som inte kan nulleras (§10.2.2, §10.2.3, §10.2.4, §10.2.11, §10.3.2 och §10.3.3) finns följande nullbara konverteringar:

  • En implicit eller explicit konvertering från S? till T?
  • En implicit eller explicit konvertering från S till T?
  • En explicit konvertering från S? till T.

En nullbar konvertering klassificeras i sig som en implicit eller explicit konvertering.

Vissa nullbara konverteringar klassificeras som standardkonverteringar och kan ske som en del av en användardefinierad konvertering. Mer specifikt klassificeras alla implicita nullbara konverteringar som implicita standardkonverteringar (§10.4.2) och de explicita nullbara konverteringar som uppfyller kraven i §10.4.3 klassificeras som explicita standardkonverteringar.

Utvärdering av en nullbar konvertering baserat på en underliggande konvertering från S till T intäkter enligt följande:

  • Om den nullbara konverteringen är från S? till T?:
    • Om källvärdet är null (HasValue egenskapen är false), är resultatet nullvärdet av typen T?.
    • I annat fall utvärderas konverteringen som en avskrivning från S? till S, följt av den underliggande konverteringen från S till T, följt av en omslutning från T till T?.
  • Om den nullbara konverteringen är från S till T?utvärderas konverteringen som den underliggande konverteringen från S till T följt av en omslutning från T till T?.
  • Om den nullbara konverteringen är från S? till Tutvärderas konverteringen som en avskrivning från S? till S följt av den underliggande konverteringen från S till T.

10.6.2 Hävda konverteringar

Med tanke på en användardefinierad konverteringsoperator som konverterar från en värdetyp S som inte kan nollställas till en värdetyp Tsom inte är null finns det en hävd konverteringsoperator som konverterar från S? till T?. Den här hävda konverteringsoperatorn utför en avskrivning från S? till S följt av den användardefinierade konverteringen från S till T följt av en omslutning från T till T?, förutom att ett nullvärde S? konverteras direkt till ett nullvärde T?. En hävd konverteringsoperator har samma implicita eller explicita klassificering som den underliggande användardefinierade konverteringsoperatorn.

10.7 Anonyma funktionskonverteringar

10.7.1 Allmänt

En anonymous_method_expression eller lambda_expression klassificeras som en anonym funktion (§12.19). Uttrycket har ingen typ, men kan implicit konverteras till en kompatibel ombudstyp. Vissa lambda-uttryck kan också implicit konverteras till en kompatibel uttrycksträdstyp.

Mer specifikt är en anonym funktion F kompatibel med en delegerad typ D som tillhandahålls:

  • Om F innehåller en anonymous_function_signature och F har D samma antal parametrar.
  • Om F inte innehåller en anonymous_function_signature kan det D finnas noll eller fler parametrar av någon typ, så länge ingen parameter D är en utdataparameter.
  • Om F har en uttryckligen angiven parameterlista har varje parameter i D samma modifierare som motsvarande parameter i F och en identitetskonvertering finns mellan motsvarande parameter i F.
  • Om F har en implicit typad parameterlista D har du inga referens- eller utdataparametrar.
  • Om brödtexten F i är ett uttryck och antingenD har en ogiltig returtyp ellerF är asynkron och D har en «TaskType» returtyp (§15.15.1), är brödtexten F i Dett giltigt uttryck (w.r.t §12) som F skulle tillåtas som en statement_expression (§13.7).
  • Om brödtexten F i är ett block och antingenD har en ogiltig returtyp ellerF är asynkron och D har en «TaskType» returtyp , är brödtexten F i Dett giltigt block (w.r.t §13.3) där ingen return instruktion anger ett uttryck när varje parameter F anges.
  • Om brödtexten F i är ett uttryck och antingenF är icke-asynkron och D har en icke-returtyp Tvoid , ellerF är asynkron och D har en «TaskType»<T> returtyp (§15.15.1), är brödtexten F i Dett giltigt uttryck (w.r.t §12) som F implicit kan konverteras till .T
  • Om brödtexten F i är ett block och antingenF är icke-asynkron och D har en icke-void returtyp T, ellerF är asynkron och D har en «TaskType»<T> returtyp, är brödtexten F i Dett giltigt instruktionsblock (w.r.t §13.3) med F en icke-nåbar slutpunkt där varje retursats anger ett uttryck som implicit kan konverteras till T.

Exempel: Följande exempel illustrerar dessa regler:

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";
};

slutexempel

Exempel: Exemplen som följer använder en allmän ombudstyp Func<A,R> som representerar en funktion som tar ett argument av typen A och returnerar ett värde av typen R:

delegate R Func<A,R>(A arg);

I tilldelningarna

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

parametern och returtyperna för varje anonym funktion bestäms utifrån vilken typ av variabel som den anonyma funktionen tilldelas till.

Den första tilldelningen konverterar den anonyma funktionen till ombudstypen Func<int,int> eftersom när x är given typ intx + 1 är ett giltigt uttryck som implicit kan konverteras till typen int.

På samma sätt konverterar den andra tilldelningen den anonyma funktionen till delegattypen Func int,double> eftersom resultatet av (av x + 1 typen int) implicit kan konverteras till typen double.<

Den tredje tilldelningen är dock ett kompileringsfel eftersom resultatet av (av x + 1 typen ) inte implicit kan konverteras till typen intnär x den är given typdoubledouble.

Den fjärde tilldelningen konverterar den anonyma asynkrona funktionen till delegattypen Func<int, Task<int>> eftersom resultatet av (av x + 1 typen int) implicit konverteras till den effektiva returtypen int för async lambda, som har en returtyp Task<int>.

slutexempel

Ett lambda-uttryck F är kompatibelt med en uttrycksträdstyp Expression<D> om F det är kompatibelt med ombudstypen D. Detta gäller inte för anonyma metoder, bara lambda-uttryck.

Anonyma funktioner kan påverka överbelastningsmatchning och delta i typinferens. Mer information finns i §12.6 .

10.7.2 Utvärdering av anonyma funktionskonverteringar till ombudstyper

Konvertering av en anonym funktion till en delegattyp skapar en delegatinstans som refererar till den anonyma funktionen och den (eventuellt tomma) uppsättningen av insamlade yttre variabler som är aktiva vid tidpunkten för utvärderingen. När ombudet anropas körs brödtexten för den anonyma funktionen. Koden i brödtexten körs med hjälp av den uppsättning insamlade yttre variabler som refereras av ombudet. En delegate_creation_expression (§12.8.17.6) kan användas som en alternativ syntax för att konvertera en anonym metod till en ombudstyp.

Anropslistan för ett ombud som skapats från en anonym funktion innehåller en enda post. Ombudets exakta målobjekt och målmetod är ospecificerade. I synnerhet är det ospecificerat om målobjektet för ombudet är null, this värdet för den omslutande funktionsmedlemmen eller något annat objekt.

Konverteringar av semantiskt identiska anonyma funktioner med samma (eventuellt tomma) uppsättning insamlade yttre variabelinstanser till samma ombudstyper tillåts (men krävs inte) för att returnera samma delegatinstans. Termen semantiskt identisk används här för att innebära att körningen av anonyma funktioner i samtliga fall ger samma effekter med samma argument. Den här regeln tillåter att kod som följande optimeras.

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));
        ...
    }
}

Eftersom de två anonyma funktionsdelegaterna har samma (tomma) uppsättning insamlade yttre variabler, och eftersom de anonyma funktionerna är semantiskt identiska, tillåts kompilatorn att låta ombuden referera till samma målmetod. Kompilatorn kan faktiskt returnera samma delegatinstans från båda anonyma funktionsuttrycken.

10.7.3 Utvärdering av lambda-uttryckskonverteringar till uttrycksträdstyper

Konvertering av ett lambda-uttryck till en uttrycksträdstyp skapar ett uttrycksträd (§8.6). Mer exakt ger utvärderingen av lambda-uttryckskonverteringen en objektstruktur som representerar själva lambda-uttryckets struktur.

Alla lambda-uttryck kan inte konverteras till uttrycksträdstyper. Konverteringen till en kompatibel delegattyp finns alltid, men den kan misslyckas vid kompileringstid av implementeringsdefinierade orsaker.

Obs! Vanliga orsaker till att ett lambda-uttryck inte kan konverteras till en uttrycksträdstyp är:

  • Den har en blocktext
  • Den har modifieraren async
  • Den innehåller en tilldelningsoperator
  • Den innehåller en utdata- eller referensparameter
  • Den innehåller ett dynamiskt bundet uttryck

slutkommentar

10.8 Gruppkonverteringar för metod

Det finns en implicit konvertering från en metodgrupp (§12.2) till en kompatibel delegattyp (§20.4). Om D är en ombudstyp och E är ett uttryck som klassificeras som en metodgrupp är det D kompatibelt med E om och endast om E innehåller minst en metod som är tillämplig i sin normala form (§12.6.4.2) på alla argumentlistor (§12.6.2) med typer och modifierare som matchar parametertyperna och modifierarna Di , enligt beskrivningen i följande.

Kompileringstidsapplikationen för konverteringen från en metodgrupp E till en ombudstyp D beskrivs i följande.

  • En enskild metod M väljs som motsvarar ett metodanrop (§12.8.10.2) i formuläret E(A), med följande ändringar:
    • Argumentlistan A är en lista över uttryck, var och en klassificerad som en variabel och med typen och modifieraren (in, out, eller ref) för motsvarande parameter i parameter_list av D , förutom parametrar av typen dynamic, där motsvarande uttryck har typen object i stället för dynamic.
    • De kandidatmetoder som beaktas är endast de metoder som är tillämpliga i sin normala form och inte utelämnar några valfria parametrar (§12.6.4.2). Därför ignoreras kandidatmetoder om de endast är tillämpliga i sin utökade form, eller om en eller flera av deras valfria parametrar inte har någon motsvarande parameter i D.
  • En konvertering anses finnas om algoritmen i §12.8.10.2 producerar en enda bästa metod M som är kompatibel (§20.4) med D.
  • Om den valda metoden M är en instansmetod avgör instansuttrycket som är associerat med E ombudets målobjekt.
  • Om den valda metoden M är en tilläggsmetod som anges med hjälp av en medlemsåtkomst i ett instansuttryck, avgör instansuttrycket ombudets målobjekt.
  • Resultatet av konverteringen är ett värde av typen D, nämligen ett ombud som refererar till den valda metoden och målobjektet.

Exempel: Följande visar metodgruppkonverteringar:

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
    }
}

Tilldelningen konverterar d1 implicit metodgruppen F till ett värde av typen D1.

Tilldelningen som d2 visar hur det är möjligt att skapa ett ombud till en metod som har mindre härledda (kontravarianta) parametertyper och en mer härledd (covariant) returtyp.

Tilldelningen som d3 visar hur ingen konvertering finns om metoden inte är tillämplig.

Tilldelningen som d4 visar hur metoden måste vara tillämplig i sin normala form.

Tilldelningen som d5 visar hur parameter- och returtyper för ombudet och metoden endast tillåts skilja sig åt för referenstyper.

slutexempel

Precis som med alla andra implicita och explicita konverteringar kan cast-operatorn användas för att explicit utföra en viss konvertering.

Exempel: Därför är exemplet

object obj = new EventHandler(myDialog.OkClick);

kan skrivas i stället

object obj = (EventHandler)myDialog.OkClick;

slutexempel

En metodgruppkonvertering kan referera till en allmän metod, antingen genom att uttryckligen ange typargument inom Eeller via typinferens (§12.6.3). Om typinferens används används parametertyperna för ombudet som argumenttyper i slutsatsdragningsprocessen. Returtypen för ombudet används inte för slutsatsdragning. Oavsett om typargumenten anges eller härleds ingår de i metodgruppens konverteringsprocess. det här är de typargument som används för att anropa målmetoden när det resulterande ombudet anropas.

Exempel:

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
    }
}

slutexempel

Metodgrupper kan påverka överbelastningsmatchning och delta i typinferens. Mer information finns i §12.6 .

Körningsutvärderingen av en metodgruppkonvertering fortsätter på följande sätt:

  • Om den metod som valts vid kompileringstid är en instansmetod, eller om det är en tilläggsmetod som används som en instansmetod, bestäms målobjektet för ombudet från instansuttrycket som är associerat med E:
    • Instansuttrycket utvärderas. Om den här utvärderingen orsakar ett undantag körs inga ytterligare steg.
    • Om instansuttrycket är av en reference_type blir värdet som beräknas av instansuttrycket målobjektet. Om den valda metoden är en instansmetod och målobjektet är nullgenereras en System.NullReferenceException och inga ytterligare steg körs.
    • Om instansuttrycket är av en value_type utförs en boxningsåtgärd (§10.2.9) för att konvertera värdet till ett objekt, och det här objektet blir målobjektet.
  • Annars är den valda metoden en del av ett statiskt metodanrop och målobjektet för ombudet är null.
  • En delegatinstans av ombudstyp D hämtas med en referens till metoden som fastställdes vid kompileringstiden och en referens till målobjektet som beräknades ovan enligt följande:
    • Konverteringen tillåts (men krävs inte) för att använda en befintlig ombudsinstans som redan innehåller dessa referenser.
    • Om en befintlig instans inte återanvänds skapas en ny (§20.5). Om det inte finns tillräckligt med minne tillgängligt för att allokera den nya instansen genereras en System.OutOfMemoryException . Annars initieras instansen med de angivna referenserna.