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 typlong
implicit, så uttryck av typenint
kan implicit behandlas som typlong
. Den motsatta konverteringen, från typlong
till typint
, ä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
ochT
, för alla typerT
. - Mellan
T
ochT?
för alla referenstyperT
. - Mellan
object
ochdynamic
. - 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
ocht3
alla har två element: enint
följt av enstring
. Tuppelns elementtyper kan själva utföras av tupplar, som it4
,t5
ocht6
. Det finns en identitetskonvertering mellan varje par av motsvarande elementtyper, inklusive kapslade tupplar, och därför finns det en identitetskonvertering mellan typerna av tupplart4
,t5
ocht6
.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
tillshort
,int
,long
,float
,double
ellerdecimal
. - Från
byte
tillshort
,ushort
,int
,uint
,long
,ulong
,float
, ellerdouble
decimal
. - Från
short
tillint
,long
,float
,double
ellerdecimal
. - Från
ushort
tillint
,uint
,long
,ulong
,float
,double
ellerdecimal
. - Från
int
tilllong
,float
,double
ellerdecimal
. - Från
uint
tilllong
,ulong
,float
,double
ellerdecimal
. - Från
long
tillfloat
,double
ellerdecimal
. - Från
ulong
tillfloat
,double
ellerdecimal
. - Från
char
tillushort
,int
,uint
,long
,ulong
,float
,double
ellerdecimal
. - Från
float
tilldouble
.
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
ochdynamic
. - Från alla class_type
S
till alla class_typeT
, som tillhandahållsS
härleds frånT
. - Från alla class_type
S
till alla interface_typeT
, tillhandahållnaS
implementerarT
. - Från alla interface_type
S
till alla interface_typeT
, som tillhandahållsS
härleds frånT
. - Från en array_type
S
med en elementtypSᵢ
till en array_typeT
med en elementtypTᵢ
, förutsatt att allt följande är sant:S
ochT
skiljer sig endast i elementtyp. Med andra ord ochS
T
har samma antal dimensioner.- Det finns en implicit referenskonvertering från
Sᵢ
tillTᵢ
.
- Från en endimensionell matristyp
S[]
tillSystem.Collections.Generic.IList<T>
,System.Collections.Generic.IReadOnlyList<T>
och deras basgränssnitt, förutsatt att det finns en implicit identitet eller referenskonvertering frånS
tillT
. - 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_typeT₀
ochT₀
har en identitetskonvertering tillT
. - 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 ombudstypT₀
ochT₀
är varianskonverterad (§18.2.3.3) tillT
. - 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_typeI₀
ochI₀
har en identitetskonvertering tillI
. - 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_typeI₀
, ochI₀
är varians-cabriolet (§18.2.3.3) tillI
. - 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änssnittI
, med en boxningsklass med namnetS_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 typenS
består nu av att köra uttrycketnew S_Boxing(v)
och returnera den resulterande instansen som ett värde för konverteringens måltyp. Det innebär att påståendenaS 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örningstypS
, och en körningstypkontroll med operatornis
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 typenobject
. Följande är till exempelstruct 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
tillbox
gör att värdetp
för kopieras. HadePoint
deklarerats som enclass
i stället skulle värdet 20 vara utdata eftersomp
ochbox
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 object
och 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
ochi
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örningstypend
(string
) till måltypen. En konvertering hittas tillstring
men inte tillint
.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 typensbyte
, ,short
byte
,ushort
,uint
ellerulong
, förutsatt att värdet för constant_expression ligger inom måltypens intervall. - En constant_expression av typen
long
kan konverteras till typenulong
, 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 basklassC
, frånT
till valfri basklass avC
, och frånT
till alla gränssnitt som implementeras avC
. - Från
T
till en interface_typeI
iT
den effektiva gränssnittsuppsättningen och frånT
till valfritt basgränssnitt förI
. - Från
T
till en typparameterU
somT
är beroende avU
(§15.2.5).Obs! Eftersom
T
är känt som en referenstyp är körningstypenU
alltid en referenstyp inom omfångetT
för , även omU
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 basklassC
, frånT
till valfri basklass avC
, och frånT
till alla gränssnitt som implementeras avC
.Obs! kommer att vara en av typerna
System.Object
,System.ValueType
ellerSystem.Enum
(annarsT
skulle vara känd för att vara en referenstyp).C
slutkommentar - Från
T
till en interface_typeI
iT
den effektiva gränssnittsuppsättningen och frånT
till valfritt basgränssnitt förI
.
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 referenstypS
om den har en implicit konvertering till en referenstypS₀
ochS₀
har en identitetskonvertering tillS
. Vid körning körs konverteringen på samma sätt som konverteringen tillS₀
. - Från
T
till en gränssnittstypI
om den har en implicit konvertering till en gränssnittstypI₀
ochI₀
är varianskonverterad tillI
(§18.2.3.3). Vid körning, omT
ä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 T
motsvarande 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
,t2
t4
ocht5
är alla giltiga, eftersom implicita konverteringar finns från elementuttrycken till motsvarande elementtyper. Deklarationen avt3
är ogiltig eftersom det inte finns någon konvertering frånnull
tillint
. Deklarationen avt5
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
tillbyte
,ushort
,uint
,ulong
ellerchar
. - Från
byte
tillsbyte
ellerchar
. - Från
short
tillsbyte
,byte
,ushort
,uint
,ulong
ellerchar
. - Från
ushort
tillsbyte
,byte
,short
ellerchar
. - Från
int
tillsbyte
,byte
,short
,ushort
,uint
,ulong
ellerchar
. - Från
uint
tillsbyte
,byte
,short
,ushort
,int
ellerchar
. - Från
long
tillsbyte
,byte
,short
,ushort
,int
,uint
,ulong
ellerchar
. - Från
ulong
tillsbyte
,byte
,short
,ushort
,int
,uint
,long
ellerchar
. - Från
char
tillsbyte
,byte
ellershort
. - Från
float
tillsbyte
,byte
,short
,ushort
,int
,uint
,long
,ulong
, ,char
ellerdecimal
. - Från
double
tillsbyte
,byte
,short
,ushort
,int
,uint
,long
,ulong
,char
, ,float
ellerdecimal
. - Från
decimal
tillsbyte
,byte
,short
,ushort
,int
,uint
,long
,ulong
,char
, ,float
ellerdouble
.
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 ettSystem.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
- I ett
- 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 ettSystem.OverflowException
. - För en konvertering från
float
ellerdouble
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
.
- Om operandvärdet är NaN eller oändligt genereras en
- 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.
- I en kontrollerad kontext fortsätter konverteringen enligt följande:
- För en konvertering från
double
tillfloat
double
avrundas värdet till närmastefloat
värde. Om värdetdouble
är för litet för att representeras som ettfloat
blir resultatet noll med samma tecken som värdet. Om värdetsdouble
storlek är för stor för att representeras som enfloat
blir resultatet oändligt med samma tecken som värdet. Om värdetdouble
är NaN blir resultatet även NaN. - För en konvertering från
float
ellerdouble
tilldecimal
konverteras källvärdet tilldecimal
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
decimal
blir resultatet noll, vilket bevarar tecknet för det ursprungliga värdet omdecimal
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.
- Om källvärdet är för litet för att representeras som ett
- För en konvertering från
decimal
tillfloat
ellerdouble
decimal
avrundas värdet till närmastedouble
värde ellerfloat
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 intervalletfloat
för ochdouble
, men är inte garanterat. Fördecimal
representationer utan infiniteter eller NaN-värden, och med ett intervall som är mindre änfloat
, blir resultatet av en konvertering fråndecimal
till antingenfloat
ellerdouble
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
, ,double
ellerdecimal
till någon enum_type. - Från alla enum_type till
sbyte
,byte
,short
ushort
, ,int
,uint
,long
,ulong
, ,char
,float
, ,double
ellerdecimal
. - 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 avint
bearbetas en konvertering frånE
tillbyte
som en explicit numerisk konvertering (§10.3.2) frånint
tillbyte
och en konvertering frånbyte
tillE
bearbetas som en implicit numerisk konvertering (§10.2.3) frånbyte
tillint
. 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_typeT
, som tillhandahållsS
är en basklass förT
. - Från alla class_type
S
till alla interface_typeT
, tillhandahållsS
inte är förseglad och tillhandahållsS
inte implementerarT
. - Från alla interface_type
S
till alla class_typeT
, tillhandahållsT
inte är förseglade eller tillhandahållnaT
S
redskap . - Från alla interface_type
S
till alla interface_typeT
, som tillhandahållsS
härleds inte frånT
. - Från en array_type
S
med en elementtypSᵢ
till en array_typeT
med en elementtypTᵢ
, förutsatt att allt följande är sant:S
ochT
skiljer sig endast i elementtyp. Med andra ord ochS
T
har samma antal dimensioner.- Det finns en explicit referenskonvertering från
Sᵢ
tillTᵢ
.
- 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ånS
tillT
.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 matristypT[]
, förutsatt att det finns en identitetskonvertering eller explicit referenskonvertering frånS
till T. - Från
System.Delegate
och de gränssnitt som implementeras till alla delegate_type. - Från en referenstyp
S
till en referenstypT
om den har en explicit referenskonvertering frånS
till en referenstypT₀
ochT₀
det finns en identitetskonvertering frånT₀
tillT
. - Från en referenstyp
S
till ett gränssnitt eller en ombudstypT
om det finns en explicit referenskonvertering frånS
till ett gränssnitt eller en ombudstypT₀
och antingenT₀
är varianskonverterad tillT
ellerT
är varianskonverterad tillT₀
§18.2.3.3. - Från
D<S₁...Sᵥ>
tillD<T₁...Tᵥ>
därD<X₁...Xᵥ>
är en allmän ombudstyp,D<S₁...Sᵥ>
är inte kompatibel med eller identiskD<T₁...Tᵥ>
med , och för varje typparameterXᵢ
avD
följande undantag:- Om
Xᵢ
är invariant är detSᵢ
identiskt medTᵢ
. - Om
Xᵢ
är covariant finns det en identitetskonvertering, implicit referenskonvertering eller explicit referenskonvertering frånSᵢ
tillTᵢ
. - Om
Xᵢ
är kontravariantSᵢ
är deTᵢ
antingen identiska eller båda referenstyperna.
- Om
- 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 T
motsvarande 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_typeI₀
till non_nullable_value-typen och en identitetskonvertering frånI
tillI₀
. - Från alla interface_type
I
till alla non_nullable_value_type där det sker en avboxningskonvertering från en interface_typeI₀
till non_nullable_value_type och antingenI₀
är variance_convertible tillI
ellerI
är varianskonverterad tillI₀
(§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åendenaobject 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 tillT
och från valfri basklassC
tillT
. - Från alla interface_type till
T
. - Från
T
till alla interface_typeI
förutsatt att det inte redan finns en implicit referenskonvertering frånT
tillI
. - Från en type_parameter
U
tillT
somT
är beroende avU
(§15.2.5).Obs! Eftersom
T
är känd för att vara en referenstyp, inom omfångetT
för , kommer körningstypen för dig alltid att vara en referenstyp, även omU
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 tillT
och från valfri basklassC
tillT
.Obs! C är en av typerna
System.Object
,System.ValueType
ellerSystem.Enum
(annarsT
ä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_typeI
förutsatt att det inte redan finns en implicit konvertering frånT
tillI
. Denna konvertering består av en implicit boxningskonvertering (§10.2.9) frånT
tillobject
följt av en explicit referenskonvertering frånobject
tillI
. Vid körning, omT
är en värdetyp, körs konverteringen som en boxningskonvertering följt av en explicit referenskonvertering. Vid körning, omT
är en referenstyp, körs konverteringen som en explicit referenskonvertering. - Från en typparameter
U
till angivet somT
ärU
beroende av (§15.2.5T
). Vid körning, omT
är en värdetyp ochU
är en referenstyp, körs konverteringen som en konvertering som avboxas. Vid körning, om bådeT
ochU
är värdetyper, såT
är ochU
nödvändigtvis samma typ och ingen konvertering utförs. Vid körning, omT
är en referenstyp, är detU
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
tilllong
var tillåten kan man enkelt förvänta sig att detX<int>.F(7)
skulle returnera7L
. 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 rutaint
inte kan konverteras direkt till enlong
.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 typB
, så finns det en explicit standardkonvertering från typA
till typB
och från typB
till typA
. 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₀
ochT₀
är olika typer.- Antingen
S₀
ellerT₀
ä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
tillT
eller frånT
tillS
.
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 typB
, och om varkenA
ellerB
interface_types
,A
så sägs den omfattas avB
, ochB
sägs omfatta .A
- Om det finns en implicit standardkonvertering (§10.4.2) från ett uttryck
E
till en typB
, och om varkenB
eller typen avE
(om den har en) är interface_types
, sägs denE
omfattas avB
ochB
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₀
ochT₀
.- Om
E
har en typ, låt varaS
den typen. - Om
S
ellerT
är nullbara värdetyper, låtSᵢ
ochTᵢ
vara deras underliggande typer, annars låtSᵢ
ochTᵢ
varaS
ochT
, respektive. - Om
Sᵢ
ellerTᵢ
är typparametrar, låtS₀
ochT₀
vara deras effektiva basklasser, annars låtS₀
ochT₀
varaSₓ
ochTᵢ
, respektive.
- Om
Leta reda på vilken uppsättning typer,
D
, som användardefinierade konverteringsoperatorer ska övervägas från. Den här uppsättningen består avS₀
(omS₀
finns och är en klass eller struct), basklassernaS₀
för (omS₀
finns och är en klass) ochT₀
(omT₀
är en klass eller struct). En typ läggs bara till i uppsättningenD
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 iD
som konverterar från en typ som omfattarE
till en typ som omfattas avT
. OmU
är tom är konverteringen odefinierad och ett kompileringsfel inträffar.- Om
S
finns och någon av operatorerna iU
konvertera frånS
ärS
.Sₓ
- Annars
Sₓ
är den mest omfattande typen i den kombinerade uppsättningen av källtyper för operatorerna iU
. Om exakt en mest omfattande typ inte kan hittas är konverteringen tvetydig och ett kompileringsfel inträffar.
- Om
Hitta den mest specifika måltypen,
Tₓ
, för operatorerna iU
:- Om någon av operatorerna konverteras
U
tillT
ärT
.Tₓ
- Annars
Tₓ
är den mest omfattande typen i den kombinerade uppsättningen av måltyper för operatorerna iU
. Om exakt en mest omfattande typ inte kan hittas är konverteringen tvetydig och ett kompileringsfel inträffar.
- Om någon av operatorerna konverteras
Hitta den mest specifika konverteringsoperatorn:
- Om
U
innehåller exakt en användardefinierad konverteringsoperator som konverterar frånSₓ
tillTₓ
är detta den mest specifika konverteringsoperatorn. - Om
U
innehåller exakt en hävd konverteringsoperator som konverterar frånSₓ
tillTₓ
är detta annars den mest specifika konverteringsoperatorn. - Annars är konverteringen tvetydig och ett kompileringsfel inträffar.
- Om
Tillämpa slutligen konverteringen:
- Om E inte redan har typen
Sₓ
utförs en implicit standardkonvertering frånE
tillSₓ
. - Den mest specifika konverteringsoperatorn anropas för att konvertera från
Sₓ
tillTₓ
. - Om
Tₓ
inteT
utförs en implicit standardkonvertering frånTₓ
tillT
.
- Om E inte redan har typen
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₀
ochT₀
.- Om
E
har en typ, låt varaS
den typen. - Om
S
ellerT
är nullbara värdetyper, låtSᵢ
ochTᵢ
vara deras underliggande typer, annars låtSᵢ
ochTᵢ
varaS
ochT
, respektive. - Om
Sᵢ
ellerTᵢ
är typparametrar, låtS₀
ochT₀
vara deras effektiva basklasser, annars låtS₀
ochT₀
varaSᵢ
ochTᵢ
, respektive.
- Om
- 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 (omS₀
finns och är en klass eller struct), basklassernaS₀
för (omS₀
finns och är en klass),T₀
(omT₀
är en klass eller struct) och basklassernaT₀
för (omT₀
är enS₀
klass).A
-typen läggs endast till i uppsättningenD
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 iD
som konverterar från en typ som omfattarE
eller omfattas avS
(om den finns) till en typ som omfattar eller omfattas avT
. OmU
är tom är konverteringen odefinierad och ett kompileringsfel inträffar. - Hitta den mest specifika källtypen,
Sₓ
, för operatorerna iU
:- Om S finns och någon av operatorerna i
U
konvertera frånS
ärS
.Sₓ
- Om någon av operatorerna i
U
konverterar från typer som omfattarE
är annarsSₓ
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 iU
. Om exakt en mest omfattande typ inte kan hittas är konverteringen tvetydig och ett kompileringsfel inträffar.
- Om S finns och någon av operatorerna i
- Hitta den mest specifika måltypen,
Tₓ
, för operatorerna iU
:- Om någon av operatorerna konverteras
U
tillT
ärT
.Tₓ
- Om någon av operatorerna i
U
konverterar till typer som omfattas avT
är annarsTₓ
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 iU
. Om ingen mest omfattande typ kan hittas är konverteringen tvetydig och ett kompileringsfel inträffar.
- Om någon av operatorerna konverteras
- Hitta den mest specifika konverteringsoperatorn:
- Om U innehåller exakt en användardefinierad konverteringsoperator som konverterar från
Sₓ
tillTₓ
är detta den mest specifika konverteringsoperatorn. - Om
U
innehåller exakt en hävd konverteringsoperator som konverterar frånSₓ
tillTₓ
är detta annars den mest specifika konverteringsoperatorn. - Annars är konverteringen tvetydig och ett kompileringsfel inträffar.
- Om U innehåller exakt en användardefinierad konverteringsoperator som konverterar från
- Tillämpa slutligen konverteringen:
- Om
E
inte redan har typenSₓ
utförs en explicit standardkonvertering från E tillSₓ
. - Den mest specifika användardefinierade konverteringsoperatorn anropas för att konvertera från
Sₓ
tillTₓ
. - Om
Tₓ
inteT
utförs en explicit standardkonvertering frånTₓ
tillT
.
- Om
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?
tillT?
- En implicit eller explicit konvertering från
S
tillT?
- En explicit konvertering från
S?
tillT
.
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?
tillT?
:- Om källvärdet är null (
HasValue
egenskapen ärfalse
), är resultatet nullvärdet av typenT?
. - I annat fall utvärderas konverteringen som en avskrivning från
S?
tillS
, följt av den underliggande konverteringen frånS
tillT
, följt av en omslutning frånT
tillT?
.
- Om källvärdet är null (
- Om den nullbara konverteringen är från
S
tillT?
utvärderas konverteringen som den underliggande konverteringen frånS
tillT
följt av en omslutning frånT
tillT?
. - Om den nullbara konverteringen är från
S?
tillT
utvärderas konverteringen som en avskrivning frånS?
tillS
följt av den underliggande konverteringen frånS
tillT
.
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 T
som 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 ochF
harD
samma antal parametrar. - Om
F
inte innehåller en anonymous_function_signature kan detD
finnas noll eller fler parametrar av någon typ, så länge ingen parameterD
är en utdataparameter. - Om
F
har en uttryckligen angiven parameterlista har varje parameter iD
samma modifierare som motsvarande parameter iF
och en identitetskonvertering finns mellan motsvarande parameter iF
. - Om
F
har en implicit typad parameterlistaD
har du inga referens- eller utdataparametrar. - Om brödtexten
F
i är ett uttryck och antingenD
har en ogiltig returtyp ellerF
är asynkron ochD
har en«TaskType»
returtyp (§15.15.1), är brödtextenF
iD
ett giltigt uttryck (w.r.t §12) somF
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 ochD
har en«TaskType»
returtyp , är brödtextenF
iD
ett giltigt block (w.r.t §13.3) där ingenreturn
instruktion anger ett uttryck när varje parameterF
anges. - Om brödtexten
F
i är ett uttryck och antingenF
är icke-asynkron ochD
har en icke-returtypT
void
, ellerF
är asynkron ochD
har en«TaskType»<T>
returtyp (§15.15.1), är brödtextenF
iD
ett giltigt uttryck (w.r.t §12) somF
implicit kan konverteras till .T
- Om brödtexten
F
i är ett block och antingenF
är icke-asynkron ochD
har en icke-void returtypT
, ellerF
är asynkron ochD
har en«TaskType»<T>
returtyp, är brödtextenF
iD
ett giltigt instruktionsblock (w.r.t §13.3) medF
en icke-nåbar slutpunkt där varje retursats anger ett uttryck som implicit kan konverteras tillT
.
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 typenA
och returnerar ett värde av typenR
: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ärx
är given typint
x + 1
är ett giltigt uttryck som implicit kan konverteras till typenint
.På samma sätt konverterar den andra tilldelningen den anonyma funktionen till delegattypen Func int,double> eftersom resultatet av (av
x + 1
typenint
) implicit kan konverteras till typendouble
.<Den tredje tilldelningen är dock ett kompileringsfel eftersom resultatet av (av
x + 1
typen ) inte implicit kan konverteras till typenint
närx
den är given typdouble
double
.Den fjärde tilldelningen konverterar den anonyma asynkrona funktionen till delegattypen
Func<int, Task<int>>
eftersom resultatet av (avx + 1
typenint
) implicit konverteras till den effektiva returtypenint
för async lambda, som har en returtypTask<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 D
i , 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äretE(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
, ellerref
) för motsvarande parameter i parameter_list avD
, förutom parametrar av typendynamic
, där motsvarande uttryck har typenobject
i stället fördynamic
. - 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
.
- Argumentlistan
- En konvertering anses finnas om algoritmen i §12.8.10.2 producerar en enda bästa metod
M
som är kompatibel (§20.4) medD
. - Om den valda metoden
M
är en instansmetod avgör instansuttrycket som är associerat medE
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 metodgruppenF
till ett värde av typenD1
.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 E
eller 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
null
genereras enSystem.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.
ECMA C# draft specification