Sdílet prostřednictvím


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 typ long je implicitní, takže výrazy typu int mohou být implicitně považovány za typ long. Opačný převod typu na typ longintje 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 and T, for any type T.
  • Mezi T a T? pro libovolný typ odkazu T.
  • Mezi object a dynamic.
  • 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 t1t2t3intstring Typy elementů řazené kolekce členů mohou být samy o sobě, jako v t4, t5a t6. 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ých t4kolekcí členů a t5t6.

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 , , shortint, long, , floatnebo double.decimal
  • Od byte do , short, , ushort, , int, uintlongulongfloatnebo .doubledecimal
  • Od short do , int, long, float, nebo double.decimal
  • Od ushort do , , int, uint, long, , ulong, floatnebo double.decimal
  • Od int do long, float, doublenebo decimal.
  • Od uint do , long, ulong, float, nebo double.decimal
  • Od long do float, doublenebo decimal.
  • Od ulong do float, doublenebo decimal.
  • Od char do , ushort, , int, uint, , long, ulong, float, nebo double.decimal
  • Od float do double.

Převody z int, , uintlongnebo 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 a dynamic.
  • Z jakéhokoli S do libovolného T , je odvozeno S od T.
  • Z libovolného T , poskytnuté S implementuje .T
  • Z jakéhokoli S do libovolného T , je odvozeno S od T.
  • S s typem Sᵢ elementu do array_typeT s typem Tᵢelementu jsou splněny všechny následující podmínky:
    • S a T liší se pouze v typu prvku. Jinými slovy, S a T mají stejný počet dimenzí.
    • Implicitní převod odkazu existuje z Sᵢ do Tᵢ.
  • Z jednorozměrného typu S[] pole na System.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 z ST.
  • 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₀ a T₀ má převod identity na T.
  • Z jakéhokoli reference_type na rozhraní nebo typ T delegáta, pokud má implicitní identitu nebo odkaz na typ T₀ rozhraní nebo delegáta a T₀ je variance-sklápěcí (§18.2.3.3) na T.
  • 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_typeI tak, že existuje převod boxingu z non_nullable_value_type na jiný interface_typeI₀a I₀ má převod identity na .I
  • Z jakéhokoli non_nullable_value_type na jakýkoli interface_typeI takový, že existuje krabicový převod z non_nullable_value_type na jiný I₀ a I₀ 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í Is boxovací třídou s názvem S_Boxing.

interface I
{
    void M();
}

struct S : I
{
    public void M() { ... }
}

sealed class S_Boxing : I
{
    S value;

    public S_Boxing(S value)
    {
        this.value = value;
    }

    public void M()
    {
        value.M();
    }
}

Boxing a value v of type S now contains of executing the expression new S_Boxing(v) and returning the výsledný instance as a value of the target type of the conversion. Proto příkazy

S 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 typ Smodulu 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ý typ object. 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 k box příčinám kopírování hodnoty p . Byla Point deklarována místo class toho, hodnota 20 by byla výstupem, protože p by box 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 objecta 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ím i 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 typu druntime (string) na cílový typ. Převod je nalezen, string ale nikoli intna .

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 typ sbyte, byte, short, ushort, uintnebo ulong, za předpokladu, že hodnota constant_expression je v rozsahu cílového typu.
  • Constant_expression typu long lze převést na typ ulongza 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řídy C, od T do jakékoli základní třídy C, a od T do libovolného rozhraní implementovaného C.
  • Od T interface_typeIv Tefektivní sadě rozhraní a z T libovolného základního Irozhraní .
  • Od T parametru typu U , který T závisí na U (§15.2.5).

    Poznámka: Vzhledem k tomu T , že je známo, že jde o typ odkazu, v rámci oboru Tbude typ U 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řídy C, od T do jakékoli základní třídy C, a od T do libovolného rozhraní implementovaného C.

    Poznámka: C bude jedním z typů System.Object, System.ValueTypenebo System.Enum (jinak T by bylo známo, že se jednat o odkazový typ). koncová poznámka

  • Od T interface_typeIv Tefektivní sadě rozhraní a z T libovolného základního Irozhraní .

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 Ttypu existují následující další implicitní převody:

  • Od T typu odkazu S , pokud má implicitní převod na typ S₀ odkazu a S₀ má převod identity na S. Za běhu se převod provede stejným způsobem jako převod na S₀.
  • Typ T rozhraní I , pokud má implicitní převod na typ I₀rozhraní a I₀ je variance-konvertibilní na I (§18.2.3.3). Pokud je to typ hodnoty za běhu T , 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 TSystem.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

t1Deklarace , t2t4 a t5 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 z nullintna . 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, nebo ulong.char
  • Od byte do sbyte nebo char.
  • Od short do , , sbytebyte, ushort, , uintnebo ulong.char
  • Od ushort do sbyte, byte, shortnebo char.
  • Od int do , , sbyte, byte, short, , ushort, uintnebo ulong.char
  • Od uint do , , sbytebyte, short, , ushortnebo int.char
  • Od long do , sbyte, , byte, short, , ushort, int, uint, nebo ulong.char
  • Od ulong do , sbyte, , byte, short, , ushort, int, uint, nebo long.char
  • Od char do sbyte, bytenebo short.
  • Od float do sbyte, byte, , short, , ushort, int, uintlongulongchar, , nebo .decimal
  • Od , , , double, , sbyte, byte, short, ushortint, uint, long, nebo ulong. charfloatdecimal
  • Od , , , decimal, , sbyte, byte, short, ushortint, uint, long, nebo ulong. charfloatdouble

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ého double 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.
    • 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.
  • Při převodu z double na floatdouble se hodnota zaokrouhlí na nejbližší float hodnotu. double Pokud je hodnota příliš malá, aby představovala jako , floatvýsledek se změní na nulu se stejným znaménkem jako hodnota. Pokud je velikost double hodnoty příliš velká, aby představovala jako , floatvý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 nebo double na decimalje zdrojová hodnota převedena na decimal 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, pokud decimal podporuje podepsané nulové hodnoty.
    • Pokud je velikost zdrojové hodnoty příliš velká, aby reprezentovala jako decimalhodnotu , 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.
  • Pro převod z decimalfloat nebo doubleje decimal hodnota zaokrouhlená na nejbližší double nebo float 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ž rozsah float a double, ale není zaručeno, že. U decimal reprezentací bez infinit nebo hodnot NaN a s rozsahem menším než floatje výsledek převodu na decimal hodnotu nebo floatdouble 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, , intuint, longulongcharfloatdoublenebo decimal do jakékoli enum_type.
  • Z libovolné enum_type na , , sbytebyteshortushortintuintlong, , ulong, , , charnebo .floatdoubledecimal
  • 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 typem int, je převod z E na byte je zpracován jako explicitní číselný převod (§10.3.2) od int do bytea převod z byte na E je zpracován jako implicitní číselný převod (§10.2.3) od byte do int. 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_typeS do libovolného class_typeT, je poskytována S základní třída T.
  • 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án T implementuje .S
  • Z jakékoli interface_typeS do jakékoli interface_typeT, poskytnuté S není odvozeno z T.
  • S s typem Sᵢ elementu do array_typeT s typem Tᵢelementu jsou splněny všechny následující podmínky:
    • S a T liší se pouze v typu prvku. Jinými slovy, S a T mají stejný počet dimenzí.
    • Explicitní převod odkazu existuje z Sᵢ do Tᵢ.
  • 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>ST
  • Z System.Collections.Generic.IList<S>, System.Collections.Generic.IReadOnlyList<S>a jejich základní rozhraní na jednorozměrný typ T[]pole , za předpokladu, že existuje převod identity nebo explicitní odkaz převod z S na T.
  • Z System.Delegate rozhraní implementuje do libovolného delegate_type.
  • Z typu S odkazu na typ odkazu T , pokud má explicitní převod odkazu na S typ T₀ odkazu a T₀ existuje převod identity z T₀ na T.
  • Z referenčního typu S na rozhraní nebo typ T delegáta, pokud existuje explicitní převod odkazu z S rozhraní nebo typu T₀ delegáta a buď T₀ je variance-sklápěcí na T nebo T je variance-sklápěcí na T₀§18.2.3.3.
  • Z D<S₁...Sᵥ> místa, kde D<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ý parametr Xᵢ typu následujících D blokování:
    • Pokud Xᵢ je invariantní, pak Sᵢ je shodný s Tᵢ.
    • Pokud Xᵢ je kovariantní, pak existuje převod identity, implicitní převod odkazu nebo explicitní převod odkazu z Sᵢ .Tᵢ
    • Pokud Xᵢ je kontravariantní, pak Sᵢ jsou Tᵢ buď identické, nebo oba odkazové typy.
  • 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 nullhodnota 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 TSystem.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í z I₀ a převod identity z I do I₀.
  • Z jakéhokoli interface_type do libovolnéhoInon_nullable_value_type, kde existuje převod rozbalení z interface_type na non_nullable_value_typeI₀ a buď I₀ je variance_convertible nebo II je variance-sklápěcí na I₀ (§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_typeS se skládá z provádění výrazu ((S_Boxing)box).value. Proto příkazy

object 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 CT do T a z jakékoli základní třídy C do T.
  • Z libovolného interface_type do T.
  • Od T jakékoli interface_typeI za předpokladu, že neexistuje implicitní převod odkazu z T na I.
  • Od type_parameterU po T poskytnutí, která T závisí na U (§15.2.5).

    Poznámka: Vzhledem k tomu T , že je známo, že se jedná o typ odkazu, v rámci oboru T, 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 CT do T a z jakékoli základní třídy C do T.

    Poznámka: C bude jedním z typů System.Object, System.ValueTypenebo System.Enum (jinak T 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 z T na I. Tento převod se skládá z implicitního převodu boxingu (§10.2.9) od Tobject následného explicitního převodu na objectI. Pokud je to typ hodnoty za běhu T , 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ý TT závisí na U (§15.2.5). Je-li T to typ hodnoty a U jedná se o referenční typ, provede se převod jako převod rozbalení. 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 typ odkazu, pak U 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 na long byl povolen, jeden by mohl snadno očekávat, že X<int>.F(7) by se vrátil 7L. 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 pole int nelze převést přímo na long.

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 typ B, pak standardní explicitní převod existuje z typu na typ AB a z typu B na typ A. 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 ST, 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 ST pouze v případě, že jsou splněny všechny následující podmínky:

  • S₀ a T₀ jsou různé typy.
  • Buď S₀ nebo je třída nebo T₀ 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 do T nebo z T do S.

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.2A
  • Pokud existuje standardní implicitní převod (§10.4.2) z výrazu na typ E, a pokud ani B typ B (pokud ho obsahuje) nejsou E, pak s je uvedeno, že zahrnujeE, a B 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 SS₀ a T₀.

    • Pokud E typ obsahuje, nechte S tento typ.
    • Pokud S nebo T jsou typy hodnot s možnou hodnotou null, let Sᵢ a Tᵢ být jejich podkladové typy, jinak let Sᵢ a be Tᵢ a ST, v uvedeném pořadí.
    • Pokud Sᵢ nebo Tᵢ jsou parametry typu, let S₀ a T₀ být jejich efektivní základní třídy, jinak let S₀ a být T₀ a SₓTᵢ , v uvedeném pořadí.
  • Najděte sadu typů, Dze kterých se budou považovat uživatelem definované operátory převodu. Tato sada se skládá z S₀ (pokud S₀ existuje a je třída nebo struktura), základní třídy S₀ (pokud S₀ existuje a je třída) a T₀ (pokud T₀ je třída nebo struktura). Typ se přidá do sady D 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ího E typ, který Tzahrnuje . Pokud U je prázdný, převod není definován a dojde k chybě v době kompilace.

    • Pokud S existuje a některý z operátorů v U převodu z S, pak Sₓ je S.
    • Sₓ V opačném případě je nejvíce zahrnutý typ v kombinované množině zdrojových typů operátorů v U. Pokud nelze najít přesně jeden nejobsáhodnější typ, převod je nejednoznačný a dojde k chybě v době kompilace.
  • Vyhledejte nejvýraznější cílový typ , Tₓoperátory v U:

    • Pokud některý z operátorů v U převodu na T, pak Tₓ je T.
    • Tₓ V opačném případě je nejvíce zahrnující typ v kombinované množině cílových typů operátorů v U. Pokud nelze najít přesně jeden typ zahrnující nejvíce, převod je nejednoznačný a dojde k chybě v době kompilace.
  • 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ₓ na Tₓ, 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í z Sₓ na Tₓ, 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.
  • Nakonec použijte převod:

    • Pokud E typ ještě nemá Sₓ, provede se standardní implicitní převod z E na Sₓ .
    • Nejvýraznější operátor převodu je vyvolán pro převod z Sₓ na Tₓ.
    • Pokud Tₓ tomu tak není T, provede se standardní implicitní převod z Tₓ na T .

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 SS₀ a T₀.
    • Pokud E typ obsahuje, nechte S tento typ.
    • Pokud S nebo T jsou typy hodnot s možnou hodnotou null, let Sᵢ a Tᵢ být jejich podkladové typy, jinak let Sᵢ a be Tᵢ a ST, v uvedeném pořadí.
    • Pokud Sᵢ nebo Tᵢ jsou parametry typu, let S₀ a T₀ být jejich efektivní základní třídy, jinak let S₀ a být T₀ a SᵢTᵢ , v uvedeném pořadí.
  • Najděte sadu typů, Dze kterých se budou považovat uživatelem definované operátory převodu. Tato sada se skládá z S₀ (pokud S₀ existuje a je třída nebo struktura), základní třídy S₀ (pokud S₀ existuje a je třída), T₀ (pokud T₀ je třída nebo struktura) a základní třídy T₀ (pokud T₀ 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ího E nebo zahrnutého S (pokud existuje) na typ zahrnující nebo zahrnující T. Pokud U je prázdný, převod není definován a dojde k chybě v době kompilace.
  • Vyhledejte nejvýraznější typ zdroje , Sₓoperátory v U:
    • Pokud S existuje a některý z operátorů v U převodu z S, pak Sₓ je S.
    • Jinak platí, že pokud některý z operátorů U , které převedou na typy, které zahrnují E, je nejzahrnulejším Sₓ 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ů v U. Pokud nelze najít přesně jeden typ zahrnující nejvíce, převod je nejednoznačný a dojde k chybě v době kompilace.
  • Vyhledejte nejvýraznější cílový typ , Tₓoperátory v U:
    • Pokud některý z operátorů v U převodu na T, pak Tₓ je T.
    • V opačném případě platí, že pokud některý z operátorů převedou U na typy, které jsou zahrnuty T, pak Tₓ 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ů v U. Pokud nelze najít nejobsáhodnější typ, převod je nejednoznačný a dojde k chybě v době kompilace.
  • 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ₓ na Tₓ, 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í z Sₓ na Tₓ, 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.
  • Nakonec použijte převod:
    • Pokud E typ ještě nemá Sₓ, provede se standardní explicitní převod z E na Sₓ .
    • Nejvýraznější uživatelem definovaný operátor převodu je vyvolán pro převod z Sₓ na Tₓ.
    • Pokud Tₓ tomu tak není T, provede se standardní explicitní převod z Tₓ na T .

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 ST?
  • Explicitní převod z S? na T.

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 je false), výsledek je null hodnota typu T?.
    • V opačném případě se převod vyhodnotí jako rozbalení z S? do S, následované podkladovým převodem z S na T, následované zabalením z T do T?.
  • Pokud je převod s možnou hodnotou null od S do T?, převod se vyhodnotí jako podkladový převod z S na T následný obtékání z T do T?.
  • Pokud je převod s možnou hodnotou null od S? do T, převod se vyhodnotí jako přepsání z S? na S následný podkladový převod z S do T.

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 Thodnoty , 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 ST 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, pak D a F 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ý parametr D 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 a F mezi odpovídajícím parametrem v Fsouboru 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í a D má návratový «TaskType» typ (§15.15.1), pak když je každému parametru udělen typ odpovídajícího parametru F v D, je text platného F výrazu (w.r.t §12), který by byl povolen jako statement_expression (§13.7).
  • Pokud je tělo F bloku a D má návratový typ F je asynchronní a D má návratový «TaskType» typ , pak když je každému parametru F zadán typ odpovídajícího parametru v D, je text F 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í a D má nevrácenývoid typ T, F je asynchronní a D«TaskType»<T> návratový typ (§15.15.1), pak když je každému parametru F přiřazen typ odpovídajícího parametru v D, je text platného F výrazu (w.r.t §12), který je implicitně přepočítnut na T.
  • Pokud je tělo F bloku a buďF je nesynchronní a D má nesynchronní návratový typ T, F je asynchronní a D«TaskType»<T> návratový typ, pak když je každému parametru udělen typ odpovídajícího parametru F v D, je text platného F 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 na T.

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 typu A a vrací hodnotu typu R:

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 pokud x je daný typ int, x + 1 je platný výraz, který je implicitně převoditelný na typ int.

Podobně druhé přiřazení úspěšně převede anonymní funkci na typ delegáta Func<int, double> , protože výsledek x + 1 (typu int) je implicitně převoditelný na typ double.

Třetí přiřazení je však chyba v době kompilace, protože pokud x je daný typ double, výsledek x + 1 (typu double) není implicitně konvertibilní na typ int.

Čtvrté přiřazení úspěšně převede anonymní asynchronní funkci na typ Func<int, Task<int>> delegáta, protože výsledek x + 1 (typu int) je implicitně převoditelný na efektivní návratový typ int asynchronní lambda, který má návratový typ Task<int>.

end example

Výraz F lambda je kompatibilní se stromovým typem Expression<D> výrazu, pokud F je kompatibilní s typem Ddelegá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áře E(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, outnebo ref) odpovídajícího parametru v parameter_listD s výjimkou parametrů typu dynamic, kde odpovídající výraz má typ object místo dynamic.
    • 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.
  • Převod je považován za existující, pokud algoritmus §12.8.10.2D
  • 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 skupinu F metod na hodnotu typu D1.

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.