10 převodů
10.1 Obecné
Převod způsobí, že se výraz převede na určitý typ nebo se s ním zachází. V prvním případě může převod zahrnovat změnu v reprezentaci. Převody mohou být implicitní nebo explicitní a určuje, zda je vyžadováno explicitní přetypování.
Příklad: Například převod z typu
int
na typlong
je implicitní, takže výrazy typuint
mohou být implicitně považovány za typlong
. Opačný převod typu na typlong
int
je explicitní a proto je vyžadován explicitní přetypování.int a = 123; long b = a; // implicit conversion from int to long int c = (int) b; // explicit conversion from long to int
end example
Některé převody jsou definovány jazykem. Programy mohou rovněž definovat vlastní převody (§10.5).
Některé převody v jazyce jsou definovány z výrazů na typy, jiné z typů na typy. Převod z typu se vztahuje na všechny výrazy, které tento typ mají.
Příklad:
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;
end example
10.2 Implicitní převody
10.2.1 Obecné
Následující převody jsou klasifikovány jako implicitní převody:
- Převody identit (§10.2.2)
- Implicitní číselné převody (§10.2.3)
- Implicitní převody výčtů (§10.2.4)
- Implicitní interpolované převody řetězců (§10.2.5)
- Implicitní převody odkazů (§10.2.8)
- Převody krabic (§10.2.9)
- Implicitní dynamické převody (§10.2.10)
- Implicitní převody parametrů typu (§10.2.12)
- Implicitní převody konstantních výrazů (§10.2.11)
- Implicitní převody definované uživatelem (včetně zrušených) (§10.2.14)
- Anonymní převody funkcí (§10.2.15)
- Převody skupin metod (§10.2.15)
- Převody literálů null (§10.2.7)
- Implicitní převody s možnou hodnotou null (§10.2.6)
- Implicitní převody řazené kolekce členů (§10.2.13)
- Výchozí převody literálů (§10.2.16)
- Implicitní převody vyvolání (§10.2.17)
Implicitní převody mohou nastat v různých situacích, včetně vyvolání členů funkce (§12.6.6), přetypování výrazů (§12.9.7) a přiřazení (§12.21).
Předem definované implicitní převody vždy proběhnou úspěšně a nikdy nezpůsobí vyvolání výjimek.
Poznámka: Správně navržené uživatelem definované implicitní převody by měly mít také tyto vlastnosti. koncová poznámka
Pro účely převodu jsou typy object
a dynamic
identity konvertibilní (§10.2.2).
Dynamické převody (§10.2.10) se však vztahují pouze na výrazy typu dynamic
(§8.2.4).
10.2.2 Převod identity
Převod identity se převede z libovolného typu na stejný typ nebo typ, který je ekvivalentní za běhu. Jedním z důvodů, proč tento převod existuje, je tak, že typ T
nebo výraz typu T
lze říci, že se má převést na T
sebe. Existují následující převody identit:
- Between
T
andT
, for any typeT
. - Mezi
T
aT?
pro libovolný typ odkazuT
. - Mezi
object
adynamic
. - Mezi všemi typy řazené kolekce členů se stejnou a odpovídající konstruovaný
ValueTuple<...>
typ, pokud existuje převod identity mezi každou dvojicí odpovídajících typů prvků. - Mezi typy vytvořenými ze stejného obecného typu, kde existuje převod identity mezi každým odpovídajícím argumentem typu.
Příklad: Následující příklad znázorňuje rekurzivní povahu třetího pravidla:
(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;
Typy řazených kolekcíčlenůch
t1
t2
t3
int
string
Typy elementů řazené kolekce členů mohou být samy o sobě, jako vt4
,t5
at6
. Mezi jednotlivými dvojicemi odpovídajících typů prvků, včetně vnořených řazených kolekcí členů, existuje převod identity mezi typy řazenýcht4
kolekcí členů at5
t6
.end example
Všechny převody identit jsou symetrické. Pokud existuje převod identity z T₁
do T₂
, pak existuje převod identity z T₂
do T₁
. Dva typy jsou konvertibilní identity, pokud existuje převod identity mezi dvěma typy.
Ve většiněpřípadůch Vzhledem k tomu, že operace s plovoucí desetinnou čárkou mohou být prováděny s vyšší přesností, než je předepsáno jejich typem (§8.3.7), může přiřazení výsledků vést ke ztrátě přesnosti a explicitní přetypování je zaručeno snížení přesnosti na to, co je předepsáno typem (§12.9.7).
10.2.3 Implicitní číselné převody
Implicitní číselné převody jsou:
- Od
sbyte
do , ,short
int
,long
, ,float
nebodouble
.decimal
- Od
byte
do ,short
, ,ushort
, ,int
,uint
long
ulong
float
nebo .double
decimal
- Od
short
do ,int
,long
,float
, nebodouble
.decimal
- Od
ushort
do , ,int
,uint
,long
, ,ulong
,float
nebodouble
.decimal
- Od
int
dolong
,float
,double
nebodecimal
. - Od
uint
do ,long
,ulong
,float
, nebodouble
.decimal
- Od
long
dofloat
,double
nebodecimal
. - Od
ulong
dofloat
,double
nebodecimal
. - Od
char
do ,ushort
, ,int
,uint
, ,long
,ulong
,float
, nebodouble
.decimal
- Od
float
dodouble
.
Převody z int
, , uint
long
nebo ulong
z float
a z long
nebo ulong
na double
mohou způsobit ztrátu přesnosti, ale nikdy nezpůsobí ztrátu velikosti. Ostatní implicitní číselné převody nikdy neztratí žádné informace.
Neexistují žádné předdefinované implicitní převody na char
typ, takže hodnoty ostatních integrálních typů se automaticky nepřevádějí na char
typ.
10.2.4 Implicitní převody výčtu
Implicitní převod výčtu umožňuje constant_expression (§12.23) s jakýmkoli typem celého čísla a nulovou hodnotou, která se má převést na jakýkoli enum_type a na všechny nullable_value_type, jejichž podkladovým typem je enum_type. V druhém případě je převod vyhodnocen převodem na podkladovou enum_type a zabalením výsledku (§8.3.12).
10.2.5 Implicitní interpolované převody řetězců
Implicitní interpolovaný převod řetězců umožňuje převést interpolated_string_expression (§12.8.3) na System.IFormattable
nebo System.FormattableString
(která implementuje System.IFormattable
).
Při použití tohoto převodu se řetězcová hodnota nevytváření z interpolovaného řetězce. Místo toho se vytvoří instance System.FormattableString
, jak je dále popsáno v §12.8.3.
10.2.6 Implicitní převody s možnou hodnotou null
Implicitní převody s možnou hodnotou null jsou převody s možnou hodnotou null (§10.6.1) odvozené z implicitních předdefinovaných převodů.
10.2.7 Převody literálů null
Implicitní převod existuje z literálu null
na jakýkoli typ odkazu nebo typ hodnoty null. Tento převod vytvoří nulový odkaz, pokud je cílovým typem odkaz nebo hodnota null (§8.3.12) daného typu hodnoty s možnou hodnotou null.
10.2.8 Implicitní převody odkazů
Implicitní převody odkazů jsou:
- Z libovolného reference_type do
object
adynamic
. - Z jakéhokoli
S
do libovolnéhoT
, je odvozenoS
odT
. - Z libovolného
T
, poskytnutéS
implementuje .T
- Z jakéhokoli
S
do libovolnéhoT
, je odvozenoS
odT
. -
S
s typemSᵢ
elementu do array_typeT
s typemTᵢ
elementu jsou splněny všechny následující podmínky:-
S
aT
liší se pouze v typu prvku. Jinými slovy,S
aT
mají stejný počet dimenzí. - Implicitní převod odkazu existuje z
Sᵢ
doTᵢ
.
-
- Z jednorozměrného typu
S[]
pole naSystem.Collections.Generic.IList<T>
,System.Collections.Generic.IReadOnlyList<T>
a jejich základní rozhraní za předpokladu, že existuje implicitní identita nebo odkaz převodu zS
T
. - Z libovolného array_type do
System.Array
a rozhraní, která implementuje. - Z libovolného delegate_type do
System.Delegate
a rozhraní, která implementuje. - Od literálu s hodnotou null (§6.4.5.7) až po libovolný typ odkazu.
- Z libovolného reference_type na
T
, pokud má implicitní identitu nebo odkaz na reference_typeT₀
aT₀
má převod identity naT
. - Z jakéhokoli reference_type na rozhraní nebo typ
T
delegáta, pokud má implicitní identitu nebo odkaz na typT₀
rozhraní nebo delegáta aT₀
je variance-sklápěcí (§18.2.3.3) naT
. - Implicitní převody zahrnující parametry typu, které jsou známé jako odkazové typy. Další podrobnosti o implicitních převodech zahrnujících parametry typu najdete v §10.2.12 .
Implicitní převody odkazů jsou tyto převody mezi reference_types, které lze prokázat, že jsou vždy úspěšné, a proto nevyžadují žádné kontroly za běhu.
Převody odkazů, implicitní nebo explicitní, nikdy nemění referenční identitu převedeného objektu.
Poznámka: Jinými slovy, zatímco převod odkazu může změnit typ odkazu, nikdy nezmění typ nebo hodnotu objektu, na který se odkazuje. koncová poznámka
10.2.9 Převody krabic
Převod boxingu umožňuje implicitně převést value_type na reference_type. Existují následující převody boxingu:
- Z libovolného value_type na typ
object
. - Z libovolného value_type na typ
System.ValueType
. - Z libovolného enum_type na typ
System.Enum
. - Z libovolného non_nullable_value_type do libovolnéhointerface_type implementovaného non_nullable_value_type.
- Z jakéhokoli non_nullable_value_type na jakýkoli interface_type
I
tak, že existuje převod boxingu z non_nullable_value_type na jiný interface_typeI₀
aI₀
má převod identity na .I
- Z jakéhokoli non_nullable_value_type na jakýkoli interface_type
I
takový, že existuje krabicový převod z non_nullable_value_type na jinýI₀
aI₀
je variance-sklápěcí (§18.2.3.3) na .I
- Z libovolného nullable_value_type do libovolného reference_type, kde existuje převod krabicového převodu z podkladového typu nullable_value_type na reference_type.
- Z parametru typu, o který není známo, že jde o odkazový typ, aby převod byl povolen §10.2.12.
Boxing a value of a non-nullable-value-type is alokace instance objektu a kopírování hodnoty do této instance.
Boxing a value of a nullable_value_type produces a null reference if it is the null value (HasValue
is false), or the result of unwrapping and boxing the underlying value otherwise.
Poznámka: Proces balení lze představit z hlediska existence boxovací třídy pro každý typ hodnoty. Představte si například implementaci
struct S
rozhraníI
s boxovací třídou s názvemS_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(); } }
Boxing a value
v
of typeS
now contains of executing the expressionnew S_Boxing(v)
and returning the výsledný instance as a value of the target type of the conversion. Proto příkazyS s = new S(); object box = s;
lze považovat za podobné:
S s = new S(); object box = new S_Boxing(s);
Představovaný typ boxingu popsaný výše ve skutečnosti neexistuje. Místo toho má boxovaná hodnota typu
S
typS
modulu runtime a kontrolu typu modulu runtime pomocíis
operátoru s typem hodnoty jako pravý operand testuje, zda je levý operand zadaná verze pravého operandu. Příklad:int i = 123; object box = i; if (box is int) { Console.Write("Box contains an int"); }
vypíše následující výstup:
Box contains an int
Převod boxingu znamená vytvoření kopie hodnoty, která se boxuje. To se liší od převodu reference_type na typ
object
, ve kterém hodnota nadále odkazuje na stejnou instanci a jednoduše je považována za méně odvozený typobject
. Například následující: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); } }
zobrazí výstup hodnoty 10 v konzole, protože implicitní operace boxingu, která nastane v přiřazení
p
kbox
příčinám kopírování hodnotyp
. BylaPoint
deklarována místoclass
toho, hodnota 20 by byla výstupem, protožep
bybox
odkazovala na stejnou instanci.Analogie třídy boxingu by neměla být použita jako více než užitečný nástroj pro znázornění toho, jak boxování funguje koncepčně. Mezi chováním popsaným v této specifikaci a chováním, které by vedlo k implementaci boxingu přesně tímto způsobem, existuje mnoho drobných rozdílů.
koncová poznámka
10.2.10 Implicitní dynamické převody
Implicitní dynamický převod existuje z výrazu typu dynamického na jakýkoli typ T
. Převod je dynamicky vázán §12.3.3, což znamená, že implicitní převod bude vyhledán za běhu od typu běhu výrazu na T
. Pokud se nenajde žádný převod, vyvolá se výjimka za běhu.
Tento implicitní převod zdánlivě porušuje radu na začátku §10.2 , že implicitní převod by nikdy neměl způsobit výjimku. Nejedná se však o samotný převod, ale o nalezení převodu, který způsobuje výjimku. Riziko výjimek za běhu je spojeno s používáním dynamické vazby. Pokud dynamická vazba převodu není žádoucí, lze výraz nejprve převést na object
a potom na požadovaný typ.
Příklad: Následující příklad znázorňuje implicitní dynamické převody:
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
Přiřazení k
s2
implicitními
dynamickým převodům a jejich použití, kdy je vazba operací pozastavena do doby běhu. Za běhu se implicitní převody hledají z typud
runtime (string
) na cílový typ. Převod je nalezen,string
ale nikoliint
na .end example
10.2.11 Implicitní převody konstantních výrazů
Převod implicitního konstantního výrazu umožňuje následující převody:
-
Typ constant_expression (§12.23)
int
lze převést na typsbyte
,byte
,short
,ushort
,uint
neboulong
, za předpokladu, že hodnota constant_expression je v rozsahu cílového typu. - Constant_expression typu
long
lze převést na typulong
za předpokladu, že hodnota constant_expression není záporná.
10.2.12 Implicitní převody zahrnující parametry typu
T
, o kterých je známo, že se jedná o referenční typ (§15.2.5), existují následující implicitní převody odkazů (§10.2.8):
- Od
T
do své účinné základní třídyC
, odT
do jakékoli základní třídyC
, a odT
do libovolného rozhraní implementovanéhoC
. - Od
T
interface_typeI
vT
efektivní sadě rozhraní a zT
libovolného základníhoI
rozhraní . - Od
T
parametru typuU
, kterýT
závisí naU
(§15.2.5).Poznámka: Vzhledem k tomu
T
, že je známo, že jde o typ odkazu, v rámci oboruT
bude typU
běhu vždy odkazový typ, i kdyžU
není známo, že se jedná o typ odkazu v době kompilace. koncová poznámka - Z literálu null (§6.4.5.7) do T.
T
, o němž není známo, že jde o odkazový typ §15.2.5, se při kompilaci považují následující převody zahrnující T
převody do balení (§10.2.9). Pokud je to typ hodnoty za běhu T
, převod se spustí jako převod boxingu. Pokud se jedná o referenční typ, provede se převod za běhu T
jako implicitní převod odkazu nebo převod identity.
- Od
T
do své účinné základní třídyC
, odT
do jakékoli základní třídyC
, a odT
do libovolného rozhraní implementovanéhoC
.Poznámka:
C
bude jedním z typůSystem.Object
,System.ValueType
neboSystem.Enum
(jinakT
by bylo známo, že se jednat o odkazový typ). koncová poznámka - Od
T
interface_typeI
vT
efektivní sadě rozhraní a zT
libovolného základníhoI
rozhraní .
U type_parameterT
, o kterých není známo, že se jedná o odkazový typ, existuje implicitní převod ze T
zadaného U
parametru T
typu závisí na U
.
T
Pokud je to typ hodnoty a U
jedná se o referenční typ, provede se převod jako převod boxingu. Pokud jsou oba T
typy U
hodnot za běhu, pak T
a U
jsou nutně stejného typu a neprovádí se žádný převod. V době běhu je-li T
referenčním typem, U
pak je nutně také referenčním typem a převod se provede jako implicitní převod odkazu nebo převod identity (§15.2.5).
Pro daný parametr T
typu existují následující další implicitní převody:
- Od
T
typu odkazuS
, pokud má implicitní převod na typS₀
odkazu aS₀
má převod identity naS
. Za běhu se převod provede stejným způsobem jako převod naS₀
. - Typ
T
rozhraníI
, pokud má implicitní převod na typI₀
rozhraní aI₀
je variance-konvertibilní naI
(§18.2.3.3). Pokud je to typ hodnoty za běhuT
, převod se spustí jako převod boxingu. V opačném případě se převod provede jako implicitní převod odkazu nebo převod identity.
Ve všech případech pravidla zajišťují, že se převod provede jako převod boxingu, pouze pokud je převod za běhu převodu z typu hodnoty na odkazový typ.
10.2.13 Implicitní převody řazené kolekce členů
Implicitní převod existuje z výrazu E
řazené kolekce členů na typ T
řazené kolekce členů, pokud E
má stejnýrit jako T
a implicitní převod existuje z každého prvku E
do odpovídajícího typu elementu v T
. Převod se provádí vytvořením instance odpovídajícího T
System.ValueTuple<...>
typu a inicializací každého z jeho polí v pořadí odleva doprava vyhodnocením odpovídajícího výrazu elementu E
řazené kolekce členů , převodem na odpovídající typ elementu T
pomocí nalezen implicitního převodu a inicializací pole s výsledkem.
Pokud název elementu ve výrazu řazené kolekce členů neodpovídá odpovídajícímu názvu elementu v typu řazené kolekce členů, musí být vydáno upozornění.
Příklad:
(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
t1
Deklarace ,t2
t4
at5
jsou všechny platné, protože implicitní převody existují z výrazů elementů na odpovídající typy elementů.t3
Deklarace je neplatná, protože neexistuje žádný převod znull
int
na .t5
Deklarace způsobuje upozornění, protože názvy prvků ve výrazu řazené kolekce členů se liší od těch v typu řazené kolekce členů.end example
10.2.14 Uživatelem definované implicitní převody
Implicitní převod definovaný uživatelem se skládá z volitelného standardního implicitního převodu následovaného spuštěním operátoru implicitního převodu definovaného uživatelem a dalšího volitelného standardního implicitního převodu. Přesná pravidla pro vyhodnocení implicitních převodů definovaných uživatelem jsou popsána v §10.5.4.
10.2.15 Převody anonymních funkcí a převody skupin metod
Anonymní funkce a skupiny metod nemají vlastní typy, ale mohou být implicitně převedeny na typy delegátů. Některé výrazy lambda mohou být navíc implicitně převedeny na typy stromu výrazů. Anonymní převody funkcí jsou podrobněji popsány v §10.7 a převody skupin metod v §10.8.
10.2.16 Výchozí převody literálů
Implicitní převod existuje z default_literal (§12.8.21) na jakýkoli typ. Tento převod vytvoří výchozí hodnotu (§9.3) odvozeného typu.
10.2.17 Implicitní vyvolání převodů
Zatímco vyvolání výrazů nemá typ, mohou být implicitně převedeny na jakýkoli typ.
10.3 Explicitní převody
10.3.1 Obecné
Následující převody jsou klasifikovány jako explicitní převody:
- Všechny implicitní převody (§10.2)
- Explicitní číselné převody (§10.3.2)
- Explicitní převody výčtů (§10.3.3)
- Explicitní převody s možnou hodnotou null (§10.3.4)
- Explicitní převody řazené kolekce členů (§10.3.6)
- Explicitní převody odkazů (§10.3.5)
- Explicitní převody rozhraní
- Zrušení převodů (§10.3.7)
- Explicitní převody parametrů typu (§10.3.8)
- Explicitní převody definované uživatelem (§10.3.9)
Explicitní převody mohou nastat ve výrazech přetypování (§12.9.7).
Sada explicitních převodů zahrnuje všechny implicitní převody.
Poznámka: To například umožňuje použít explicitní přetypování, pokud existuje implicitní převod identity, aby bylo možné vynutit výběr konkrétního přetížení metody. koncová poznámka
Explicitní převody, které nejsou implicitními převody, jsou převody, které nelze vždy ověřit jako úspěšné, převody, které jsou známy pravděpodobně ke ztrátě informací, a převody napříč doménami typů dostatečně odlišné, aby si zaslouží explicitní zápis.
10.3.2 Explicitní číselné převody
Explicitní číselné převody jsou převody z numeric_type na jiný numeric_type , pro které implicitní číselný převod (§10.2.3) ještě neexistuje:
- Od
sbyte
do ,byte
,ushort
,uint
, neboulong
.char
- Od
byte
dosbyte
nebochar
. - Od
short
do , ,sbyte
byte
,ushort
, ,uint
neboulong
.char
- Od
ushort
dosbyte
,byte
,short
nebochar
. - Od
int
do , ,sbyte
,byte
,short
, ,ushort
,uint
neboulong
.char
- Od
uint
do , ,sbyte
byte
,short
, ,ushort
neboint
.char
- Od
long
do ,sbyte
, ,byte
,short
, ,ushort
,int
,uint
, neboulong
.char
- Od
ulong
do ,sbyte
, ,byte
,short
, ,ushort
,int
,uint
, nebolong
.char
- Od
char
dosbyte
,byte
neboshort
. - Od
float
dosbyte
,byte
, ,short
, ,ushort
,int
,uint
long
ulong
char
, , nebo .decimal
- Od , , ,
double
, ,sbyte
,byte
,short
,ushort
int
,uint
,long
, neboulong
.char
float
decimal
- Od , , ,
decimal
, ,sbyte
,byte
,short
,ushort
int
,uint
,long
, neboulong
.char
float
double
Vzhledem k tomu, že explicitní převody zahrnují všechny implicitní a explicitní číselné převody, je vždy možné převést z libovolného numeric_type na jakýkoli jiný numeric_type pomocí výrazu přetypování (§12.9.7).
Explicitní číselné převody pravděpodobně ztratí informace nebo mohou způsobit vyvolání výjimek. Explicitní číselný převod se zpracuje takto:
- Při převodu celočíselného typu na jiný celočíselný typ závisí zpracování na kontextu kontroly přetečení (§12.8.20), ve kterém probíhá převod:
-
checked
V kontextu bude převod úspěšný, pokud je hodnota zdrojového operandu v rozsahu cílového typu, ale vyvoláSystem.OverflowException
chybu, pokud je hodnota zdrojového operandu mimo rozsah cílového typu. -
unchecked
V kontextu převod vždy proběhne úspěšně a pokračuje následujícím způsobem.- Pokud je zdrojový typ větší než cílový typ, je zdrojová hodnota zkrácena zrušením jeho "extra" nejvýznamnějších bitů. Výsledek se pak považuje za hodnotu cílového typu.
- Pokud je typ zdroje stejná jako cílový typ, je zdrojová hodnota považována za hodnotu cílového typu.
-
- Při převodu z
decimal
na celočíselný typ se zdrojová hodnota zaokrouhlí na nulu na nejbližší celočíselnou hodnotu a tato integrální hodnota se stane výsledkem převodu. Pokud je výsledná integrální hodnota mimo rozsah cílového typu,System.OverflowException
vyvolá se hodnota. - Při převodu z
float
celočíselnéhodouble
typu závisí zpracování na kontextu kontroly přetečení (§12.8.20), ve kterém probíhá převod:- V kontrolovaném kontextu bude převod pokračovat následujícím způsobem:
- Pokud je hodnota operandu NaN nebo nekonečná,
System.OverflowException
vyvolá se hodnota. - V opačném případě se zdrojový operand zaokrouhlí na nulu na nejbližší celočíselnou hodnotu. Pokud je tato integrální hodnota v rozsahu cílového typu, je tato hodnota výsledkem převodu.
-
System.OverflowException
V opačném případě je vyvolán.
- Pokud je hodnota operandu NaN nebo nekonečná,
- V nezaškrtnutém kontextu bude převod vždy úspěšný a bude pokračovat následujícím způsobem.
- Pokud je hodnota operandu NaN nebo nekonečná, je výsledkem převodu nezadaná hodnota cílového typu.
- V opačném případě se zdrojový operand zaokrouhlí na nulu na nejbližší celočíselnou hodnotu. Pokud je tato integrální hodnota v rozsahu cílového typu, je tato hodnota výsledkem převodu.
- V opačném případě je výsledkem převodu nezadaná hodnota cílového typu.
- V kontrolovaném kontextu bude převod pokračovat následujícím způsobem:
- Při převodu z
double
nafloat
double
se hodnota zaokrouhlí na nejbližšífloat
hodnotu.double
Pokud je hodnota příliš malá, aby představovala jako ,float
výsledek se změní na nulu se stejným znaménkem jako hodnota. Pokud je velikostdouble
hodnoty příliš velká, aby představovala jako ,float
výsledek se stane nekonečnem se stejným znaménkem jako hodnota.double
Pokud je hodnota NaN, výsledek je také NaN. - Při převodu ze
float
zdroje nebodouble
nadecimal
je zdrojová hodnota převedena nadecimal
reprezentaci a v případě potřeby se zaokrouhlí na nejbližší číslo (§8.3.8).- Pokud je zdrojová hodnota příliš malá, aby představovala jako
decimal
, výsledek se změní na nulu, zachová se znaménko původní hodnoty, pokuddecimal
podporuje podepsané nulové hodnoty. - Pokud je velikost zdrojové hodnoty příliš velká, aby reprezentovala jako
decimal
hodnotu , nebo je tato hodnota nekonečno, je výsledkem nekonečno zachování znaménka původní hodnoty, pokud desítkové vyjádření podporuje nefinity; jinak je vyvolána výjimka System.OverflowException. - Pokud je zdrojová hodnota NaN, výsledek je NaN, pokud desítková reprezentace podporuje sítě NaN; Jinak je vyvolán výjimka System.OverflowException.
- Pokud je zdrojová hodnota příliš malá, aby představovala jako
- Pro převod z
decimal
float
nebodouble
jedecimal
hodnota zaokrouhlená na nejbližšídouble
nebofloat
hodnotu. Pokud je velikost zdrojové hodnoty příliš velká, aby představovala v cílovém typu, nebo je tato hodnota nekonečno, výsledek je nekonečno zachovácí znaménko původní hodnoty. Pokud je zdrojová hodnota NaN, výsledek je NaN. I když tento převod může ztratit přesnost, nikdy nezpůsobí vyvolání výjimky.
Poznámka: Typ
decimal
není nutný k podpoře infinit nebo hodnot NaN, ale může to udělat; jeho rozsah může být menší než rozsahfloat
adouble
, ale není zaručeno, že. Udecimal
reprezentací bez infinit nebo hodnot NaN a s rozsahem menším nežfloat
je výsledek převodu nadecimal
hodnotu nebofloat
double
nikdy nebude nekonečno nebo NaN. koncová poznámka
10.3.3 Explicitní výčtové převody
Explicitní převody výčtu jsou:
- Od
sbyte
,byte
,short
,ushort
, ,int
uint
,long
ulong
char
float
double
nebodecimal
do jakékoli enum_type. - Z libovolné enum_type na , ,
sbyte
byte
short
ushort
int
uint
long
, ,ulong
, , ,char
nebo .float
double
decimal
- Z libovolného enum_type do jakéhokoli jiného enum_type.
Explicitní převod výčtu mezi dvěma typy se zpracuje tak, že zachází se všemi zúčastněnými enum_type jako se základním typem tohoto enum_type a pak provede implicitní nebo explicitní číselný převod mezi výslednými typy.
Příklad: Vzhledem k
E
s základním typemint
, je převod zE
nabyte
je zpracován jako explicitní číselný převod (§10.3.2) odint
dobyte
a převod zbyte
naE
je zpracován jako implicitní číselný převod (§10.2.3) odbyte
doint
. end example
10.3.4 Explicitní převody s možnou hodnotou null
Explicitní převody s možnou hodnotou null jsou převody s možnou hodnotou null (§10.6.1) odvozené z explicitních a implicitních předdefinovaných převodů.
10.3.5 Explicitní převody odkazů
Explicitní převody odkazů:
- Z objektu na jakýkoli jiný reference_type.
- Ze všech class_type
S
do libovolného class_typeT
, je poskytovánaS
základní třídaT
. - Z jakéhokoli
T
, poskytnutéS
není zapečetěno a poskytnutéS
není implementováno .T
- Z jakéhokoli
T
, poskytnutéT
není zapečetěno ani poskytovánT
implementuje .S
- Z jakékoli interface_type
S
do jakékoli interface_typeT
, poskytnutéS
není odvozeno zT
. -
S
s typemSᵢ
elementu do array_typeT
s typemTᵢ
elementu jsou splněny všechny následující podmínky:-
S
aT
liší se pouze v typu prvku. Jinými slovy,S
aT
mají stejný počet dimenzí. - Explicitní převod odkazu existuje z
Sᵢ
doTᵢ
.
-
- Z
System.Array
rozhraní, která implementuje, do libovolného array_type. - Z jednorozměrného array_type na , a jeho základní rozhraní za předpokladu, že existuje převod identity nebo explicitní odkaz převod z
S[]
System.Collections.Generic.IList<T>
.System.Collections.Generic.IReadOnlyList<T>
S
T
- Z
System.Collections.Generic.IList<S>
,System.Collections.Generic.IReadOnlyList<S>
a jejich základní rozhraní na jednorozměrný typT[]
pole , za předpokladu, že existuje převod identity nebo explicitní odkaz převod zS
na T. - Z
System.Delegate
rozhraní implementuje do libovolného delegate_type. - Z typu
S
odkazu na typ odkazuT
, pokud má explicitní převod odkazu naS
typT₀
odkazu aT₀
existuje převod identity zT₀
naT
. - Z referenčního typu
S
na rozhraní nebo typT
delegáta, pokud existuje explicitní převod odkazu zS
rozhraní nebo typuT₀
delegáta a buďT₀
je variance-sklápěcí naT
neboT
je variance-sklápěcí naT₀
§18.2.3.3. - Z
D<S₁...Sᵥ>
místa, kdeD<T₁...Tᵥ>
D<X₁...Xᵥ>
je obecný typ delegáta,D<S₁...Sᵥ>
není kompatibilní s nebo shodnýD<T₁...Tᵥ>
s , a pro každý parametrXᵢ
typu následujícíchD
blokování:- Pokud
Xᵢ
je invariantní, pakSᵢ
je shodný sTᵢ
. - Pokud
Xᵢ
je kovariantní, pak existuje převod identity, implicitní převod odkazu nebo explicitní převod odkazu zSᵢ
.Tᵢ
- Pokud
Xᵢ
je kontravariantní, pakSᵢ
jsouTᵢ
buď identické, nebo oba odkazové typy.
- Pokud
- Explicitní převody zahrnující parametry typu, které jsou známé jako odkazové typy. Další podrobnosti o explicitních převodech zahrnujících parametry typu naleznete v § 10.3.8.
Explicitní převody odkazů jsou převody mezi reference_types, které vyžadují kontroly za běhu, aby se zajistilo, že jsou správné.
Aby byl převod explicitního odkazu úspěšný za běhu, musí být null
hodnota zdrojového operandu nebo typ objektu odkazovaného zdrojovým operandem typu, který lze převést na cílový typ implicitním převodem odkazu (§10.2.8). Pokud se explicitní převod odkazu nezdaří, System.InvalidCastException
vyvolá se chyba.
Poznámka: Převody odkazů, implicitní nebo explicitní, nikdy nemění hodnotu samotného odkazu (§8.2.1), pouze jeho typ; ani nemění typ ani hodnotu odkazovaného objektu. koncová poznámka
10.3.6 Explicitní převody řazené kolekce členů
Explicitní převod existuje z výrazu E
řazené kolekce členů na typ T
řazené kolekce členů, pokud E
má stejnýrit jako T
a implicitní nebo explicitní převod existuje z každého prvku E
do odpovídajícího typu elementu v T
. Převod se provádí vytvořením instance odpovídajícího T
System.ValueTuple<...>
typu a inicializací každého z jeho polí v pořadí odleva doprava vyhodnocením odpovídajícího výrazu elementu E
řazené kolekce členů , převodem na odpovídající typ T
prvku pomocí nalezen explicitního převodu a inicializací pole s výsledkem.
10.3.7 Unboxing conversions
Převod unboxing umožňuje explicitně převést reference_type na value_type. Existují následující převody pro rozbalení:
- Z typu
object
do libovolného value_type. - Z typu
System.ValueType
do libovolného value_type. - Z typu
System.Enum
do libovolného enum_type. - Z libovolného interface_type do libovolného non_nullable_value_type , který implementuje interface_type.
- Z libovolného interface_type do libovolného
I
non_nullable_value_type, kde existuje převod rozbalení zI₀
a převod identity zI
doI₀
. - Z jakéhokoli interface_type do libovolného
I
non_nullable_value_type, kde existuje převod rozbalení z interface_type na non_nullable_value_typeI₀
a buďI₀
je variance_convertible neboI
I
je variance-sklápěcí naI₀
(§18.2.3.3). - Z libovolného reference_type do libovolného nullable_value_type, kde existuje převodod reference_type na podkladovou non_nullable_value_type nullable_value_type.
- Z parametru typu, o kterém není známo, že se jedná o typ hodnoty, aby převod byl povolen §10.3.8.
Operace rozbalení do non_nullable_value_type se skládá z první kontroly, že instance objektu je boxovaná hodnota daného non_nullable_value_type a poté zkopíruje hodnotu z instance.
Rozbalení na nullable_value_type vytvoří hodnotu null nullable_value_type pokud je zdrojový operand nebo zabalený výsledek rozbalení instance objektu na základní typ null
jinak.
Poznámka: Odkaz na imaginární boxovací třídu popsanou v §10.2.9, rozbalení převodu pole objektu na value_type
S
se skládá z provádění výrazu((S_Boxing)box).value
. Proto příkazyobject box = new S(); S s = (S)box;
konceptuálně odpovídají
object box = new S_Boxing(new S()); S s = ((S_Boxing)box).value;
koncová poznámka
Pro rozbalení převodu na danou non_nullable_value_type uspět za běhu musí být hodnota zdrojového operandu odkazem na boxovanou hodnotu tohoto non_nullable_value_type. Pokud je null
zdrojový operand vyvolán System.NullReferenceException
. Pokud je zdrojový operand odkazem na nekompatibilní objekt, System.InvalidCastException
vyvolá se chyba.
Při rozbalení převodu na danou nullable_value_type , která bude úspěšná za běhu, musí být hodnota zdrojového operandu null nebo odkaz na boxovanou hodnotu podkladové non_nullable_value_typenullable_value_type. Pokud je zdrojový operand odkazem na nekompatibilní objekt, System.InvalidCastException
vyvolá se chyba.
10.3.8 Explicitní převody zahrnující parametry typu
T
, o které je známo, že jde o referenční typ (§15.2.5), existují následující explicitní převody (§10.3.5):
- Od efektivní základní třídy
C
T
doT
a z jakékoli základní třídyC
doT
. - Z libovolného interface_type do
T
. - Od
T
jakékoli interface_typeI
za předpokladu, že neexistuje implicitní převod odkazu zT
naI
. -
Od type_parameter
U
poT
poskytnutí, kteráT
závisí naU
(§15.2.5).Poznámka: Vzhledem k tomu
T
, že je známo, že se jedná o typ odkazu, v rámci oboruT
, bude typ běhu vždy odkazový typ, i kdyžU
není známo, že se jedná o typ odkazu v době kompilace. koncová poznámka
T
, o němž není známo, že jde o odkazový typ (§15.2.5), jsou při kompilaci považovány za T
převody, které se týkají rozbalení (§10.3.7). Pokud je to typ hodnoty za běhu T
, provede se převod jako unboxing conversion. Pokud se jedná o typ odkazu, provede se převod za běhu T
jako explicitní převod odkazu nebo převod identity.
- Od efektivní základní třídy
C
T
doT
a z jakékoli základní třídyC
doT
.Poznámka: C bude jedním z typů
System.Object
,System.ValueType
neboSystem.Enum
(jinakT
by bylo známo, že se jednat o odkazový typ). koncová poznámka - Z libovolného interface_type do
T
.
T
, o kterých není známo, že jde o odkazový typ (§15.2.5), existují následující explicitní převody:
- Od
T
jakékoli interface_typeI
za předpokladu, že již není implicitní převod zT
naI
. Tento převod se skládá z implicitního převodu boxingu (§10.2.9) odT
object
následného explicitního převodu naobject
I
. Pokud je to typ hodnoty za běhuT
, provede se převod jako převod boxingu následovaný explicitním převodem odkazu. Pokud jde o typ odkazu,T
provede se převod za běhu jako explicitní převod odkazu. - Od parametru
U
typu, kterýT
T
závisí naU
(§15.2.5). Je-liT
to typ hodnoty aU
jedná se o referenční typ, provede se převod jako převod rozbalení. Pokud jsou obaT
typyU
hodnot za běhu, pakT
aU
jsou nutně stejného typu a neprovádí se žádný převod. V době běhu je-liT
typ odkazu, pakU
je nutně také referenčním typem a převod se provede jako explicitní převod odkazu nebo převod identity.
Ve všech případech pravidla zajišťují, aby byl převod proveden jako převod bez složky, pokud a pouze v případě, že převod je za běhu převodu z referenčního typu na typ hodnoty.
Výše uvedená pravidla neumožňují přímý explicitní převod z parametru nekontrénovaného typu na typ bez rozhraní, což může být překvapivý. Důvodem tohoto pravidla je zabránit nejasnostem a vyčistit sémantiku těchto převodů.
Příklad: Zvažte následující deklaraci:
class X<T> { public static long F(T t) { return (long)t; // Error } }
Pokud přímý explicitní převod
t
nalong
byl povolen, jeden by mohl snadno očekávat, žeX<int>.F(7)
by se vrátil7L
. To by však nebylo, protože standardní číselné převody jsou považovány pouze tehdy, když jsou typy známé jako číselné v době vazby. Aby bylo možné sémantiku smazat, musí být místo toho napsán výše uvedený příklad:class X<T> { public static long F(T t) { return (long)(object)t; // Ok, but will only work when T is long } }
Tento kód se nyní zkompiluje, ale při provádění
X<int>.F(7)
by pak vyvolal výjimku za běhu, protože poleint
nelze převést přímo nalong
.end example
10.3.9 Explicitní převody definované uživatelem
Explicitní převod definovaný uživatelem se skládá z volitelného standardního explicitního převodu, po kterém následuje spuštění uživatelem definovaného implicitního nebo explicitního převodního operátoru následovaného dalším volitelným standardním explicitním převodem. Přesná pravidla pro vyhodnocení explicitních převodů definovaných uživatelem jsou popsána v §10.5.5.
10.4 Standardní převody
10.4.1 Obecné
Standardní převody jsou ty předdefinované převody, ke kterým může dojít jako součást převodu definovaného uživatelem.
10.4.2 Standardní implicitní převody
Následující implicitní převody jsou klasifikovány jako standardní implicitní převody:
- Převody identit (§10.2.2)
- Implicitní číselné převody (§10.2.3)
- Implicitní převody s možnou hodnotou null (§10.2.6)
- Převody literálů null (§10.2.7)
- Implicitní převody odkazů (§10.2.8)
- Převody krabic (§10.2.9)
- Implicitní převody konstantních výrazů (§10.2.11)
- Implicitní převody zahrnující parametry typu (§10.2.12)
Standardní implicitní převody výslovně vylučují implicitní převody definované uživatelem.
10.4.3 Standardní explicitní převody
Standardní explicitní převody jsou všechny standardní implicitní převody plus podmnožina explicitních převodů, pro které existuje opačný standardní implicitní převod.
Poznámka: Jinými slovy, pokud standardní implicitní převod existuje z typu
A
na typB
, pak standardní explicitní převod existuje z typu na typA
B
a z typuB
na typA
. koncová poznámka
10.5 Převody definované uživatelem
10.5.1 Obecné
Jazyk C# umožňuje rozšíření předem definovaných implicitních a explicitních převodů pomocí uživatelsky definovaných převodů. Uživatelem definované převody se zavádějí deklarací operátorů převodu (§15.10.4) ve třídách a typech struktur.
10.5.2 Povolené uživatelem definované převody
Jazyk C# umožňuje deklarovat pouze určité uživatelem definované převody. Konkrétně není možné předefinovat již existující implicitní nebo explicitní převod.
Pro daný typ zdroje a cílový typ S
T
, pokud S
nebo T
jsou typy hodnot null, let S₀
a T₀
odkazovat na jejich základní typy, jinak S₀
a T₀
jsou rovny S
a T
v uvedeném pořadí. Třída nebo struktura je povolena deklarovat převod ze zdrojového typu na cílový typ S
T
pouze v případě, že jsou splněny všechny následující podmínky:
-
S₀
aT₀
jsou různé typy. - Buď
S₀
nebo je třída neboT₀
typ struktury, ve které se provádí deklarace operátoru. - Ani
S₀
interface_typeT₀
. - S výjimkou uživatelem definovaných převodů neexistuje převod z
S
doT
nebo zT
doS
.
Omezení, která se vztahují na uživatelem definované převody, jsou uvedena v §15.10.4.
10.5.3 Vyhodnocení uživatelsky definovaných převodů
Převod definovaný uživatelem převede zdrojový výraz, který může mít typ zdroje, na jiný typ, který se nazývá cílový typ. Vyhodnocení uživatelem definovaných konverzních center při hledání nejvýraznějšího uživatelem definovaného operátoru převodu pro zdrojový výraz a cílový typ. Toto určení je rozděleno do několika kroků:
- Nalezení sady tříd a struktur, ze kterých se budou považovat uživatelem definované operátory převodu. Tato sada se skládá ze zdrojového typu a jeho základních tříd, pokud existuje zdrojový typ spolu s cílovým typem a jeho základními třídami. Pro tento účel se předpokládá, že pouze třídy a struktury mohou deklarovat uživatelem definované operátory a že typy bez tříd nemají žádné základní třídy. Pokud je zdrojový nebo cílový typ typu nullable-value-type, použije se místo toho jejich základní typ.
- Z této sady typů určíte, které operátory převodu definované uživatelem a které se používají. Aby byl operátor převodu použitelný, je možné provést standardní převod (§10.4) ze zdrojového výrazu na typ operandu operátora a musí být možné provést standardní převod z typu výsledku operátoru na cílový typ.
- Ze sady použitelných uživatelsky definovaných operátorů určuje, který operátor je jednoznačně nejvýraznější. Obecně platí, že nejvýraznější operátor je operátor, jehož typ operandu je "nejblíže" ke zdrojovému výrazu a jehož typ výsledku je "nejblíže" cílovému typu. Uživatelem definované operátory převodu se preferují před operátory přepočítání metodou lifted. Přesná pravidla pro vytvoření nejpřesnějšího uživatelem definovaného operátoru převodu jsou definována v následujících dílčích návazcích.
Jakmile identifikujete nejvýraznějšího uživatelem definovaného operátoru převodu, skutečné spuštění uživatelem definovaného převodu zahrnuje až tři kroky:
- Nejprve v případě potřeby provedete standardní převod ze zdrojového výrazu na typ operandu definovaného uživatelem nebo zvedaným operátorem převodu.
- Dále vyvolání uživatelem definovaného nebo zvednutého operátoru převodu k provedení převodu.
- V případě potřeby proveďte standardní převod z typu výsledku uživatelem definovaného operátoru převodu na cílový typ.
Vyhodnocení uživatelem definovaného převodu nikdy nezahrnuje více než jeden uživatelem definovaný nebo lifted převodní operátor. Jinými slovy, převod z typu S
na typ T
nikdy nespustí uživatelsky definovaný převod z S
do X
a pak spustí uživatelsky definovaný převod z X
do T
.
- Přesné definice vyhodnocení implicitních nebo explicitních převodů definovaných uživatelem jsou uvedeny v následujících dílčích počtech. Definice používají následující termíny:
- Pokud existuje standardní implicitní převod (§10.4.2
A
- Pokud existuje standardní implicitní převod (§10.4.2) z výrazu na typ
E
, a pokud aniB
typB
(pokud ho obsahuje) nejsouE
, paks
je uvedeno, že zahrnujeE
, aB
má zahrnovatB
.E
- Nejobsahující typ v sadě typů je jeden typ, který zahrnuje všechny ostatní typy v sadě. Pokud žádný typ nezahrne všechny ostatní typy, sada neobsahuje nejobsahující typ. Intuitivnějším způsobem je nejobsáhlejší typ "největší" v sadě – typ, na který lze implicitně převést každý z ostatních typů.
- Nejobsáhodnější typ v sadě typů je jeden typ, který je zahrnut všemi ostatními typy v sadě. Pokud žádný jeden typ nezahrne všechny ostatní typy, sada neobsahuje nejobsáženější typ. Intuitivnějším způsobem je nejobsáhlejší typ "nejmenší" v sadě – jeden typ, který lze implicitně převést na každý z ostatních typů.
10.5.4 Uživatelem definované implicitní převody
Implicitní převod z výrazu E
na typ T
definovaný uživatelem se zpracuje následujícím způsobem:
Určete typy
S
S₀
aT₀
.- Pokud
E
typ obsahuje, nechteS
tento typ. - Pokud
S
neboT
jsou typy hodnot s možnou hodnotou null, letSᵢ
aTᵢ
být jejich podkladové typy, jinak letSᵢ
a beTᵢ
aS
T
, v uvedeném pořadí. - Pokud
Sᵢ
neboTᵢ
jsou parametry typu, letS₀
aT₀
být jejich efektivní základní třídy, jinak letS₀
a býtT₀
aSₓ
Tᵢ
, v uvedeném pořadí.
- Pokud
Najděte sadu typů,
D
ze kterých se budou považovat uživatelem definované operátory převodu. Tato sada se skládá zS₀
(pokudS₀
existuje a je třída nebo struktura), základní třídyS₀
(pokudS₀
existuje a je třída) aT₀
(pokudT₀
je třída nebo struktura). Typ se přidá do sadyD
pouze v případě, že převod identity na jiný typ, který už je součástí sady, neexistuje.Vyhledejte sadu použitelných uživatelsky definovaných a zvednutých operátorů převodu.
U
Tato sada se skládá z uživatelem definovaných a zvednutí implicitních konverzních operátorů deklarovaných třídami nebo strukturami,D
které převádějí z typu zahrnujícíhoE
typ, kterýT
zahrnuje . PokudU
je prázdný, převod není definován a dojde k chybě v době kompilace.- Pokud
S
existuje a některý z operátorů vU
převodu zS
, pakSₓ
jeS
. -
Sₓ
V opačném případě je nejvíce zahrnutý typ v kombinované množině zdrojových typů operátorů vU
. Pokud nelze najít přesně jeden nejobsáhodnější typ, převod je nejednoznačný a dojde k chybě v době kompilace.
- Pokud
Vyhledejte nejvýraznější cílový typ ,
Tₓ
operátory vU
:- Pokud některý z operátorů v
U
převodu naT
, pakTₓ
jeT
. -
Tₓ
V opačném případě je nejvíce zahrnující typ v kombinované množině cílových typů operátorů vU
. Pokud nelze najít přesně jeden typ zahrnující nejvíce, převod je nejednoznačný a dojde k chybě v době kompilace.
- Pokud některý z operátorů v
Vyhledejte nejvýraznější operátor převodu:
- Pokud
U
obsahuje přesně jeden uživatelem definovaný operátor převodu, který se převede zSₓ
naTₓ
, pak je to nejvýraznější operátor převodu. - V opačném případě, pokud
U
obsahuje přesně jeden zdvižený převodní operátor, který převádí zSₓ
naTₓ
, pak je to nejvýraznější převodní operátor. - V opačném případě je převod nejednoznačný a dojde k chybě v době kompilace.
- Pokud
Nakonec použijte převod:
- Pokud E typ ještě nemá
Sₓ
, provede se standardní implicitní převod zE
naSₓ
. - Nejvýraznější operátor převodu je vyvolán pro převod z
Sₓ
naTₓ
. - Pokud
Tₓ
tomu tak neníT
, provede se standardní implicitní převod zTₓ
naT
.
- Pokud E typ ještě nemá
Uživatelem definovaný implicitní převod z typu S
na typ T
existuje, pokud uživatelem definovaný implicitní převod existuje z proměnné typu S
do T
.
Explicitní převody definované uživatelem 10.5.5
Explicitní převod z výrazu E
na typ T
definovaný uživatelem se zpracuje takto:
- Určete typy
S
S₀
aT₀
.- Pokud
E
typ obsahuje, nechteS
tento typ. - Pokud
S
neboT
jsou typy hodnot s možnou hodnotou null, letSᵢ
aTᵢ
být jejich podkladové typy, jinak letSᵢ
a beTᵢ
aS
T
, v uvedeném pořadí. - Pokud
Sᵢ
neboTᵢ
jsou parametry typu, letS₀
aT₀
být jejich efektivní základní třídy, jinak letS₀
a býtT₀
aSᵢ
Tᵢ
, v uvedeném pořadí.
- Pokud
- Najděte sadu typů,
D
ze kterých se budou považovat uživatelem definované operátory převodu. Tato sada se skládá zS₀
(pokudS₀
existuje a je třída nebo struktura), základní třídyS₀
(pokudS₀
existuje a je třída),T₀
(pokudT₀
je třída nebo struktura) a základní třídyT₀
(pokudT₀
je třída).A
Typ je přidán do sady pouze v případě, že převod identity na jiný typ již zahrnutý v saděD
neexistuje. - Vyhledejte sadu použitelných uživatelsky definovaných a zvednutých operátorů převodu.
U
Tato sada se skládá z uživatelem definovaných a zvednutí implicitních nebo explicitních konverzních operátorů deklarovaných třídami nebo strukturami,D
které převádějí z typu zahrnujícíhoE
nebo zahrnutéhoS
(pokud existuje) na typ zahrnující nebo zahrnujícíT
. PokudU
je prázdný, převod není definován a dojde k chybě v době kompilace. - Vyhledejte nejvýraznější typ zdroje ,
Sₓ
operátory vU
:- Pokud S existuje a některý z operátorů v
U
převodu zS
, pakSₓ
jeS
. - Jinak platí, že pokud některý z operátorů
U
, které převedou na typy, které zahrnujíE
, je nejzahrnulejšímSₓ
typem v kombinované sadě zdrojových typů těchto operátorů. Pokud nelze najít nejobsáhodnější typ, převod je nejednoznačný a dojde k chybě v době kompilace. -
Sₓ
V opačném případě je nejvíce zahrnující typ v kombinované množině zdrojových typů operátorů vU
. Pokud nelze najít přesně jeden typ zahrnující nejvíce, převod je nejednoznačný a dojde k chybě v době kompilace.
- Pokud S existuje a některý z operátorů v
- Vyhledejte nejvýraznější cílový typ ,
Tₓ
operátory vU
:- Pokud některý z operátorů v
U
převodu naT
, pakTₓ
jeT
. - V opačném případě platí, že pokud některý z operátorů převedou
U
na typy, které jsou zahrnutyT
, pakTₓ
je nejvíce zahrnující typ v kombinované množině cílových typů těchto operátorů. Pokud nelze najít přesně jeden typ zahrnující nejvíce, převod je nejednoznačný a dojde k chybě v době kompilace. -
Tₓ
V opačném případě je nejobsáhodnější typ v kombinované množině cílových typů operátorů vU
. Pokud nelze najít nejobsáhodnější typ, převod je nejednoznačný a dojde k chybě v době kompilace.
- Pokud některý z operátorů v
- Vyhledejte nejvýraznější operátor převodu:
- Pokud U obsahuje přesně jeden uživatelem definovaný operátor převodu, který se převede z
Sₓ
naTₓ
, pak je to nejvýraznější operátor převodu. - V opačném případě, pokud
U
obsahuje přesně jeden zdvižený převodní operátor, který převádí zSₓ
naTₓ
, pak je to nejvýraznější převodní operátor. - V opačném případě je převod nejednoznačný a dojde k chybě v době kompilace.
- Pokud U obsahuje přesně jeden uživatelem definovaný operátor převodu, který se převede z
- Nakonec použijte převod:
- Pokud
E
typ ještě nemáSₓ
, provede se standardní explicitní převod z E naSₓ
. - Nejvýraznější uživatelem definovaný operátor převodu je vyvolán pro převod z
Sₓ
naTₓ
. - Pokud
Tₓ
tomu tak neníT
, provede se standardní explicitní převod zTₓ
naT
.
- Pokud
Uživatelem definovaný explicitní převod z typu S
na typ T
existuje, pokud uživatelem definovaný explicitní převod existuje z proměnné typu S
na T
.
10.6 Převody zahrnující typy s možnou hodnotou null
10.6.1 Převody s možnou hodnotou Null
Převody s možnou hodnotou null umožňují předdefinované převody , které pracují s typy hodnot bez hodnoty null, které se mají použít také s tvary s možnou hodnotou null těchto typů. Pro každý z předdefinovaných implicitních nebo explicitních převodů, které převedou typ hodnoty bez S
hodnoty null na typ T
hodnoty, který není nullable (§10.2.2, §10.2.3, §10.2.4, §10.2.11, §10.3.2 a §10.3.3), existují následující převody s možnou hodnotou null:
- Implicitní nebo explicitní převod z
S?
T?
- Implicitní nebo explicitní převod z
S
T?
- Explicitní převod z
S?
naT
.
Převod s možnou hodnotou null je sám klasifikován jako implicitní nebo explicitní převod.
Některé převody s možnou hodnotou null jsou klasifikovány jako standardní převody a mohou nastat jako součást uživatelem definovaného převodu. Konkrétně všechny implicitní převody s možnou hodnotou null jsou klasifikovány jako standardní implicitní převody (§10.4.2) a tyto explicitní převody s možnou hodnotou null, které splňují požadavky §10.4.3 , jsou klasifikovány jako standardní explicitní převody.
Vyhodnocení převodu s možnou hodnotou null na základě podkladového převodu z S
výnosu T
následujícím způsobem:
- Pokud je převod s možnou hodnotou null:
S?
T?
- Pokud je zdrojová hodnota null (
HasValue
vlastnost jefalse
), výsledek je null hodnota typuT?
. - V opačném případě se převod vyhodnotí jako rozbalení z
S?
doS
, následované podkladovým převodem zS
naT
, následované zabalením zT
doT?
.
- Pokud je zdrojová hodnota null (
- Pokud je převod s možnou hodnotou null od
S
doT?
, převod se vyhodnotí jako podkladový převod zS
naT
následný obtékání zT
doT?
. - Pokud je převod s možnou hodnotou null od
S?
doT
, převod se vyhodnotí jako přepsání zS?
naS
následný podkladový převod zS
doT
.
10.6.2 Zvedané převody
Vzhledem k tomu, že uživatelem definovaný převodní operátor, který převádí z nenulovatelného typu S
hodnoty na nenulový typ T
hodnoty , existuje operátor převést z S?
na T?
. Tento operátor zrušeného převodu provede rozbalení z S?
na S
základě uživatelem definovaného převodu na S
T
zalamování z T
do T?
, s výjimkou toho, že hodnota S?
null se převede přímo na hodnotu null s T?
hodnotou null . Operátor lifted conversion má stejnou implicitní nebo explicitní klasifikaci jako základní uživatelem definovaný operátor převodu.
10.7 Převody anonymních funkcí
10.7.1 Obecné
Anonymous_method_expression nebo lambda_expression je klasifikován jako anonymní funkce (§12.19). Výraz nemá typ, ale lze jej implicitně převést na kompatibilní typ delegáta. Některé výrazy lambda mohou být také implicitně převedeny na kompatibilní typ stromu výrazu.
Anonymní funkce F
je konkrétně kompatibilní s zadaným typem D
delegáta:
- Pokud
F
obsahuje anonymous_function_signature, pakD
aF
mít stejný počet parametrů. - Pokud
F
neobsahuje anonymous_function_signature,D
může mít nula nebo více parametrů libovolného typu, pokud žádný parametrD
není výstupním parametrem. - Pokud
F
má explicitně zadaný seznam parametrů, každý parametr máD
stejné modifikátory jako odpovídající parametr aF
mezi odpovídajícím parametrem vF
souboru existuje převod identity. - Pokud
F
obsahuje implicitně zadaný seznam parametrů,D
nemá žádné odkazy ani výstupní parametry. - Pokud je tělo
F
výrazu a buďD
má návratový typ void, neboF
je asynchronní aD
má návratový«TaskType»
typ (§15.15.1), pak když je každému parametru udělen typ odpovídajícího parametruF
vD
, je text platnéhoF
výrazu (w.r.t §12), který by byl povolen jako statement_expression (§13.7). - Pokud je tělo
F
bloku aD
má návratový typF
je asynchronní aD
má návratový«TaskType»
typ , pak když je každému parametruF
zadán typ odpovídajícího parametru vD
, je textF
platného bloku (w.r.t §13.3), ve kterém žádnýreturn
příkaz neurčuje výraz. - Pokud je tělo
F
výrazu a buďF
je nesynchronní aD
má nevrácenývoid
typT
,F
je asynchronní aD
má«TaskType»<T>
návratový typ (§15.15.1), pak když je každému parametruF
přiřazen typ odpovídajícího parametru vD
, je text platnéhoF
výrazu (w.r.t §12), který je implicitně přepočítnut naT
. - Pokud je tělo
F
bloku a buďF
je nesynchronní aD
má nesynchronní návratový typT
,F
je asynchronní aD
má«TaskType»<T>
návratový typ, pak když je každému parametru udělen typ odpovídajícího parametruF
vD
, je text platnéhoF
bloku příkazu (w.r.t §13.3) s nedostupným koncovým bodem, ve kterém každý návratový příkaz určuje výraz, který je implicitně převést naT
.
Příklad: Následující příklady ilustrují tato pravidla:
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"; };
end example
Příklad: Následující příklady používají obecný typ
Func<A,R>
delegáta, který představuje funkci, která přebírá argument typuA
a vrací hodnotu typuR
:delegate R Func<A,R>(A arg);
V zadáních
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
parametr a návratové typy každé anonymní funkce jsou určeny z typu proměnné, ke které je anonymní funkce přiřazena.
První přiřazení úspěšně převede anonymní funkci na typ
Func<int,int>
delegáta, protože pokudx
je daný typint
,x + 1
je platný výraz, který je implicitně převoditelný na typint
.Podobně druhé přiřazení úspěšně převede anonymní funkci na typ delegáta Func<int, double> , protože výsledek
x + 1
(typuint
) je implicitně převoditelný na typdouble
.Třetí přiřazení je však chyba v době kompilace, protože pokud
x
je daný typdouble
, výsledekx + 1
(typudouble
) není implicitně konvertibilní na typint
.Čtvrté přiřazení úspěšně převede anonymní asynchronní funkci na typ
Func<int, Task<int>>
delegáta, protože výsledekx + 1
(typuint
) je implicitně převoditelný na efektivní návratový typint
asynchronní lambda, který má návratový typTask<int>
.end example
Výraz F
lambda je kompatibilní se stromovým typem Expression<D>
výrazu, pokud F
je kompatibilní s typem D
delegáta . To neplatí pro anonymní metody, pouze výrazy lambda.
Anonymní funkce můžou ovlivnit rozlišení přetížení a účastnit se odvozování typu. Další podrobnosti naleznete v §12.6 .
10.7.2 Vyhodnocení převodů anonymních funkcí na typy delegátů
Převod anonymní funkce na typ delegáta vytvoří instanci delegáta, která odkazuje na anonymní funkci a (pravděpodobně prázdnou) sadu zachycených vnějších proměnných, které jsou aktivní v době vyhodnocení. Při vyvolání delegáta se spustí tělo anonymní funkce. Kód v těle se spustí pomocí sady zachycených vnějších proměnných odkazovaných delegátem. Delegate_creation_expression (§12.8.17.6) lze použít jako alternativní syntaxi pro převod anonymní metody na typ delegáta.
Seznam vyvolání delegáta vytvořeného z anonymní funkce obsahuje jednu položku. Přesný cílový objekt a cílová metoda delegáta nejsou zadány. Konkrétně není určeno, zda cílový objekt delegáta je null
, this
hodnota ohraničujícího členu funkce nebo nějaký jiný objekt.
Převody sémanticky identických anonymních funkcí se stejnou (případně prázdnou) sadou zachycených instancí vnějších proměnných na stejné typy delegátů jsou povoleny (ale nevyžadují) k vrácení stejné instance delegáta. Termín sémanticky identický se zde používá k tomu, že provádění anonymních funkcí ve všech případech způsobí stejné účinky vzhledem ke stejným argumentům. Toto pravidlo povoluje optimalizaci kódu, například následující.
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));
...
}
}
Vzhledem k tomu, že dva delegáti anonymní funkce mají stejnou (prázdnou) sadu zachycených vnějších proměnných a vzhledem k tomu, že anonymní funkce jsou sémanticky identické, je povoleno, aby delegáti odkazovali na stejnou cílovou metodu. Kompilátoru je skutečně povoleno vrátit stejnou instanci delegáta z obou výrazů anonymní funkce.
10.7.3 Vyhodnocení převodů výrazů lambda na typy stromu výrazů
Převod výrazu lambda na typ stromu výrazu vytvoří strom výrazu (§8.6). Přesněji řečeno, vyhodnocení převodu výrazu lambda vytváří objektovou strukturu, která představuje strukturu samotného výrazu lambda.
Ne každý výraz lambda lze převést na typy stromu výrazů. Převod na kompatibilní typ delegáta vždy existuje, ale může selhat v době kompilace z důvodů definovaných implementací.
Poznámka: Mezi běžné důvody, proč se výraz lambda nepodaří převést na typ stromu výrazů, patří:
- Má blokové tělo.
async
Má modifikátor.- Obsahuje operátor přiřazení.
- Obsahuje výstupní nebo referenční parametr.
- Obsahuje dynamicky vázané výrazy.
koncová poznámka
10.8 Převody skupin metod
Implicitní převod existuje ze skupiny metod (§12.2) na kompatibilní typ delegáta (§20.4). Je-li D
typ delegáta a E
je výraz klasifikovaný jako skupina metod, je kompatibilní D
s E
tím, zda a pouze pokud E
obsahuje alespoň jednu metodu, která je použitelná v normální podobě (§12.6.4.2) k libovolnému seznamu argumentů (§12.6.2) s typy a modifikátory odpovídajícími typům parametrů a modifikátorům D
, jak je popsáno v následujících bodech.
Použití převodu ze skupiny E
metod na typ D
delegáta v době kompilace je popsáno v následujícím příkladu.
- Jedna metoda
M
je vybrána odpovídající vyvolání metody (§12.8.10.2) formulářeE(A)
s následujícími úpravami:- Seznam
A
argumentů je seznam výrazů, každý klasifikovaný jako proměnná a s typem a modifikátorem (in
,out
neboref
) odpovídajícího parametru v parameter_list –D
s výjimkou parametrů typudynamic
, kde odpovídající výraz má typobject
místodynamic
. - Považované metody jsou pouze metody použitelné v jejich normální podobě a nevylučují žádné volitelné parametry (§12.6.4.2). Kandidátské metody jsou tedy ignorovány, pokud jsou použitelné pouze v rozšířené podobě, nebo pokud jeden nebo více jejich volitelných parametrů nemá odpovídající parametr v
D
.
- Seznam
- Převod je považován za existující, pokud algoritmus §12.8.10.2
D
- Pokud je vybraná metoda
M
instance metoda, výraz instance přidruženýE
k určuje cílový objekt delegáta. - Pokud je vybraná metoda rozšiřující metodou
M
, která je označena pomocí přístupu člena ve výrazu instance, tento výraz instance určuje cílový objekt delegáta. - Výsledkem převodu je hodnota typu
D
, konkrétně delegát, který odkazuje na vybranou metodu a cílový objekt.
Příklad: Následující příklad ukazuje převody skupin metod:
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 } }
Přiřazení, které
d1
implicitně převede skupinuF
metod na hodnotu typuD1
.Přiřazení, které
d2
ukazuje, jak je možné vytvořit delegáta na metodu, která má méně odvozené (kontravariantní) typy parametrů a více odvozenější (kovariantní) návratový typ.Přiřazení, které
d3
ukazuje, jak neexistuje žádný převod, pokud metoda není použitelná.Přiřazení, které ukazuje
d4
, jak musí být metoda použitelná v normální podobě.Přiřazení, které
d5
ukazuje, jak se parametry a návratové typy delegáta a metody mohou lišit pouze pro odkazové typy.end example
Stejně jako u všech ostatních implicitních a explicitních převodů lze operátor přetypování použít k explicitnímu provedení konkrétního převodu.
Příklad: Příklad
object obj = new EventHandler(myDialog.OkClick);
místo toho by bylo možné napsat
object obj = (EventHandler)myDialog.OkClick;
end example
Převod skupiny metod může odkazovat na obecnou metodu, a to buď explicitním zadáním argumentů typu v rámci E
, nebo prostřednictvím odvození typu (§12.6.3). Pokud se použije odvození typu, typy parametrů delegáta se použijí jako typy argumentů v procesu odvozování. Návratový typ delegáta se nepoužívá k odvozování. Zda jsou zadány nebo odvozeny argumenty typu, jsou součástí procesu převodu skupiny metod; jedná se o argumenty typu použité k vyvolání cílové metody při vyvolání výsledného delegáta.
Příklad:
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 } }
end example
Skupiny metod mohou ovlivnit rozlišení přetížení a účastnit se odvozování typů. Další podrobnosti naleznete v §12.6 .
Vyhodnocení běhu převodu skupiny metod probíhá takto:
- Pokud je metoda vybraná při kompilaci metodou instance nebo je to rozšiřující metoda, ke které se přistupuje jako metoda instance, je cílový objekt delegáta určen z výrazu instance přidruženého k
E
:- Výraz instance se vyhodnotí. Pokud toto vyhodnocení způsobí výjimku, neprojdou žádné další kroky.
- Pokud je výraz instance reference_type, hodnota vypočítaná výrazem instance se stane cílovým objektem. Pokud je vybraná metoda instance a cílový objekt je
null
,System.NullReferenceException
je vyvolán a nejsou provedeny žádné další kroky. - Pokud je výraz instance value_type, provede se operace boxingu (§10.2.9) pro převod hodnoty na objekt a tento objekt se stane cílovým objektem.
- Jinak je vybraná metoda součástí volání statické metody a cílový objekt delegáta je
null
. - Instance delegáta typu
D
delegáta je získána s odkazem na metodu určenou v době kompilace a odkaz na cílový objekt vypočítaný výše, jak je znázorněno níže:- Převod je povolen (ale není vyžadován) k použití existující instance delegáta, která již tyto odkazy obsahuje.
- Pokud existující instance nebyla znovu použita, vytvoří se nový (§20.5). Pokud není k dispozici dostatek paměti pro přidělení nové instance,
System.OutOfMemoryException
vyvolá se vyvolá. V opačném případě se instance inicializuje s danými odkazy.
ECMA C# draft specification