Udostępnij za pośrednictwem


10 konwersji

10.1 Ogólne

Konwersja powoduje przekonwertowanie wyrażenia na określony typ lub traktowanie go jako określonego typu. W poprzednim przypadku konwersja może obejmować zmianę reprezentacji. Konwersje mogą być niejawne lub jawne, a to określa, czy jest wymagane jawne rzutowanie.

Przykład: na przykład konwersja typu na typ int long jest niejawna, więc wyrażenia typu int mogą być niejawnie traktowane jako typ long. Odwrotna konwersja, od typu do typu long int, jest jawna i dlatego wymagane jest jawne rzutowanie.

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

przykład końcowy

Niektóre konwersje są definiowane przez język. Programy mogą również definiować własne konwersje (§10.5).

Niektóre konwersje w języku są definiowane z wyrażeń na typy, a inne od typów do typów. Konwersja typu ma zastosowanie do wszystkich wyrażeń, które mają ten typ.

Przykład:

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;

przykład końcowy

10.2 Niejawne konwersje

10.2.1 Ogólne

Następujące konwersje są klasyfikowane jako niejawne konwersje:

  • Konwersje tożsamości (§10.2.2)
  • Niejawne konwersje liczbowe (§10.2.3)
  • Niejawne konwersje wyliczenia (§10.2.4)
  • Niejawne konwersje ciągów interpolowanych (§10.2.5)
  • Niejawne konwersje odwołań (§10.2.8)
  • Konwersje boksu (§10.2.9)
  • Niejawne konwersje dynamiczne (§10.2.10)
  • Niejawne konwersje parametrów typu (§10.2.12)
  • Niejawne konwersje wyrażeń stałych (§10.2.11)
  • Konwersje niejawne zdefiniowane przez użytkownika (w tym zniesione) (§10.2.14)
  • Konwersje funkcji anonimowych (§10.2.15)
  • Konwersje grup metod (§10.2.15)
  • Konwersje literału null (§10.2.7)
  • Niejawne konwersje dopuszczane do wartości null (§10.2.6)
  • Niejawne konwersje krotki (§10.2.13)
  • Domyślne konwersje literału (§10.2.16)
  • Niejawne konwersje rzutów (§10.2.17)

Konwersje niejawne mogą wystąpić w różnych sytuacjach, w tym wywołania składowych funkcji (§12.6.6), wyrażenia rzutowania (§12.9.7) i przypisania (§12.21).

Wstępnie zdefiniowane konwersje niejawne zawsze kończą się powodzeniem i nigdy nie powodują zgłaszania wyjątków.

Uwaga: Prawidłowo zaprojektowane niejawne konwersje zdefiniowane przez użytkownika powinny również wykazywać te cechy. notatka końcowa

Do celów konwersji typy object i dynamic są tożsamościami konwertowane (§10.2.2).

Jednak konwersje dynamiczne (§10.2.10) mają zastosowanie tylko do wyrażeń typu dynamic (§8.2.4).

10.2.2 Konwersja tożsamości

Konwersja tożsamości jest konwertowana z dowolnego typu na ten sam typ lub typ, który jest odpowiednikiem w czasie wykonywania. Jedną z przyczyn, dla których ta konwersja istnieje, jest taka, że typ T lub wyrażenie typu T można powiedzieć, że jest konwertowany na T siebie. Istnieją następujące konwersje tożsamości:

  • Między T i Tdla dowolnego typu T.
  • Między T i T? dla dowolnego typu Todwołania .
  • Między object i dynamic.
  • Między wszystkimi typami krotki o tej samej arity i odpowiadającym im typem skonstruowanym ValueTuple<...> , gdy istnieje konwersja tożsamości między każdą parą odpowiednich typów elementów.
  • Między typami utworzonymi z tego samego typu ogólnego, w którym istnieje konwersja tożsamości między każdym odpowiadającym argumentem typu.

Przykład: Poniżej przedstawiono cyklisywny charakter trzeciej reguły:

(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 krotki t1i t3 t2 wszystkie mają dwa elementy: po int stringnim element . Typy elementów krotki mogą się składać z krotki, tak jak w t4elementach , t5i t6. Istnieje konwersja tożsamości między każdą parą odpowiednich typów elementów, w tym zagnieżdżonych krotki, w związku z czym istnieje konwersja tożsamości między typami krotki t4, t5i t6.

przykład końcowy

Wszystkie konwersje tożsamości są symetryczne. Jeśli konwersja tożsamości istnieje z T₁ do T₂, konwersja tożsamości istnieje z T₂ do T₁. Dwa typy to tożsamość konwertowana , gdy istnieje konwersja tożsamości między dwoma typami.

W większości przypadków konwersja tożsamości nie ma wpływu na środowisko uruchomieniowe. Jednak ze względu na to, że operacje zmiennoprzecinkowe mogą być wykonywane z wyższą dokładnością niż przepisane przez ich typ (§8.3.7), przypisanie ich wyników może spowodować utratę dokładności, a jawne rzutowania mają gwarancję zmniejszenia dokładności do tego, co jest określone przez typ (§12.9.7).

10.2.3 Niejawne konwersje liczbowe

Niejawne konwersje liczbowe to:

  • Z sbyte do short, , int, floatlong, , doublelub decimal.
  • Z byte do short, , intulongushortlongfloatuintdoublelub .decimal
  • Z short do int, , floatlong, , doublelub decimal.
  • Z ushort do int, , longfloatdoubleuintulonglub .decimal
  • Z int do long, float, doublelub decimal.
  • Z uint do long, , floatulong, , doublelub decimal.
  • Z long do float, doublelub decimal.
  • Z ulong do float, doublelub decimal.
  • Z char do ushort, , uintulonglongfloatint, doublelub .decimal
  • Od float do double.

Konwersje z int, lub long uintulong do float i lub long ulong mogą spowodować double utratę precyzji, ale nigdy nie spowodują utraty wielkości. Inne niejawne konwersje liczbowe nigdy nie tracą żadnych informacji.

Nie ma wstępnie zdefiniowanych konwersji niejawnych na char typ, więc wartości innych typów całkowitych nie są automatycznie konwertowane na char typ.

10.2.4 Niejawne konwersje wyliczenia

Niejawna konwersja wyliczenia zezwala na konwersję constant_expression (§12.23) z dowolnym typem całkowitym i wartością zero, która ma zostać przekonwertowana na dowolną enum_type i na dowolny nullable_value_type, którego typem bazowym jest enum_type. W tym drugim przypadku konwersja jest obliczana przez przekonwertowanie na enum_type bazowych i zawijanie wyniku (§8.3.12).

10.2.5 Niejawne konwersje ciągów interpolowanych

Niejawna konwersja ciągów interpolowanych umożliwia konwersję interpolated_string_expression (§12.8.3) na System.IFormattable lub System.FormattableString (która implementuje System.IFormattable). Po zastosowaniu tej konwersji wartość ciągu nie składa się z ciągu interpolowanego. Zamiast tego jest tworzone wystąpienie System.FormattableString , zgodnie z opisem w §12.8.3.

10.2.6 Niejawne konwersje dopuszczane do wartości null

Niejawne konwersje dopuszczane do wartości null to konwersje dopuszczane do wartości null (§10.6.1) pochodzące z niejawnych wstępnie zdefiniowanych konwersji.

Konwersje literałów null 10.2.7

Niejawna konwersja istnieje od null literału do dowolnego typu odwołania lub typu wartości dopuszczanej do wartości null. Ta konwersja tworzy odwołanie o wartości null, jeśli typ docelowy jest typem referencyjnym lub wartością null (§8.3.12) danego typu wartości dopuszczanej do wartości null.

10.2.8 Niejawne konwersje odwołań

Niejawne konwersje odwołań to:

  • Z dowolnego reference_type do object i dynamic.
  • Z dowolnego class_type do dowolnego class_type S T , podany S jest pochodzi z .T
  • Z dowolnego class_type do dowolnego interface_type S T , dostarczone S implementuje .T
  • Z dowolnego interface_type do dowolnego interface_type S T , podany S jest pochodzi z .T
  • Z array_type S z typem Sᵢ elementu do array_type T z typem Tᵢelementu , pod warunkiem, że wszystkie następujące elementy są spełnione:
    • S i T różnią się tylko w typie elementu. Innymi słowy, S i T mają taką samą liczbę wymiarów.
    • Niejawna konwersja odwołania istnieje z Sᵢ do Tᵢ.
  • Z jednowymiarowego typu S[] tablicy do System.Collections.Generic.IList<T>, System.Collections.Generic.IReadOnlyList<T>i ich interfejsów podstawowych, pod warunkiem, że istnieje niejawna tożsamość lub konwersja odwołania z S do T.
  • Z dowolnego array_type do System.Array interfejsów, które implementuje.
  • Z dowolnego delegate_type do System.Delegate interfejsów, które implementuje.
  • Od literału null (§6.4.5.7) do dowolnego typu odwołania.
  • Z dowolnego reference_type do reference_typeT, jeśli ma niejawną tożsamość lub konwersję odwołania do reference_type T₀ i T₀ ma konwersję tożsamości na T.
  • Z dowolnego reference_type do interfejsu lub typu T delegata, jeśli ma niejawną tożsamość lub konwersję odwołania do interfejsu lub typu T₀ delegata i T₀ jest wariancja-cabrio (§18.2.3.3) do T.
  • Niejawne konwersje obejmujące parametry typu, które są znane jako typy referencyjne. Aby uzyskać więcej informacji na temat niejawnych konwersji obejmujących parametry typu, zobacz §10.2.12 .

Niejawne konwersje odwołań to konwersje między reference_types, które można udowodnić, że zawsze się powiedzie, i dlatego nie wymagają żadnych kontroli w czasie wykonywania.

Konwersje odwołań, niejawne lub jawne, nigdy nie zmieniają tożsamości referencyjnej konwertowanego obiektu.

Uwaga: innymi słowy, podczas gdy konwersja odwołania może zmienić typ odwołania, nigdy nie zmienia typu lub wartości obiektu, do której jest odwoływany. notatka końcowa

Konwersje boksu 10.2.9

Konwersja boksu umożliwia niejawną konwersję value_type na reference_type. Istnieją następujące konwersje boksu:

  • Z dowolnego value_type do typu object.
  • Z dowolnego value_type do typu System.ValueType.
  • Z dowolnego enum_type do typu System.Enum.
  • Z dowolnego non_nullable_value_type do dowolnego interface_type zaimplementowanego przez non_nullable_value_type.
  • Z dowolnego non_nullable_value_type do dowolnego interface_type I tak, że istnieje konwersja boksu z non_nullable_value_type do innego interface_type I₀i I₀ ma konwersję tożsamości na .I
  • Od dowolnego non_nullable_value_type do dowolnego interface_type I tak, że istnieje konwersja boksu z non_nullable_value_type do innego interface_type I₀ i I₀ jest wariancja-cabrio (§18.2.3.3) do I.
  • Od dowolnego nullable_value_type do dowolnego reference_type, w którym istnieje konwersja boksu z bazowego typu nullable_value_type do reference_type.
  • Z parametru typu, który nie jest znany jako typ odwołania do dowolnego typu, taki, że konwersja jest dozwolona przez §10.2.12.

Boxing wartość typu innej niż nullable-value składa się z przydzielania wystąpienia obiektu i kopiowania wartości do tego wystąpienia.

Boxing wartość nullable_value_type tworzy odwołanie o wartości null, jeśli jest to wartość null (HasValue jest fałsz) lub wynik rozpasania i tworzenia pola bazowej wartości w przeciwnym razie.

Uwaga: Proces tworzenia boksu można sobie wyobrazić pod względem istnienia klasy boksu dla każdego typu wartości. Rozważmy na przykład struct S zaimplementowanie interfejsu Iz klasą boksu o nazwie 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 wartość v typu S składa się teraz z wykonywania wyrażenia new S_Boxing(v) i zwracania wynikowego wystąpienia jako wartości typu docelowego konwersji. W związku z tym instrukcje

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

można traktować jako podobne do następujących:

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

Wyobrażany typ boksu opisany powyżej nie istnieje. Zamiast tego pole typu S ma typ środowiska uruchomieniowego i sprawdzanie typu Sśrodowiska uruchomieniowego przy użyciu is operatora z typem wartości jako prawy operand testuje, czy lewy operand jest wersją pola prawego operandu. Na przykład:

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

polecenie zwróci następujące dane wyjściowe:

Box contains an int

Konwersja boksu oznacza utworzenie kopii wartości w polu. Różni się to od konwersji reference_type na typ object, w którym wartość nadal odwołuje się do tego samego wystąpienia i po prostu jest traktowana jako mniej pochodny typ object. Na przykład następujące elementy

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

spowoduje wygenerowanie wartości 10 w konsoli programu , ponieważ niejawna operacja boxingu wykonywana w przypisaniu p box powoduje skopiowanie wartości p . Zamiast Point tego class zadeklarowano wartość 20, ponieważ p i box odwołuje się do tego samego wystąpienia.

Analogia klasy boksu nie powinna być używana jako bardziej niż przydatne narzędzie do pikturowania sposobu działania boksu koncepcyjnie. Istnieje wiele subtelnych różnic między zachowaniem opisanym przez tę specyfikację a zachowaniem, które wynikałoby z implementacji boksu właśnie w ten sposób.

notatka końcowa

10.2.10 Niejawne konwersje dynamiczne

Niejawna konwersja dynamiczna istnieje z wyrażenia typu dynamicznego do dowolnego typu T. Konwersja jest dynamicznie powiązana §12.3.3, co oznacza, że niejawna konwersja będzie wyszukiwana w czasie wykonywania z typu czasu wykonywania wyrażenia na T. Jeśli nie znaleziono konwersji, zostanie zgłoszony wyjątek czasu wykonywania.

Ta niejawna konwersja pozornie narusza porady na początku §10.2 , że niejawna konwersja nigdy nie powinna powodować wyjątku. Nie jest to jednak sama konwersja, ale znalezienie konwersji powoduje wyjątek. Ryzyko wyjątków w czasie wykonywania jest związane z użyciem powiązania dynamicznego. Jeśli powiązanie dynamiczne konwersji nie jest pożądane, wyrażenie można najpierw przekonwertować na object, a następnie na żądany typ.

Przykład: Poniżej przedstawiono niejawne konwersje dynamiczne:

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

Przypisania do s2 i i oba stosują niejawne konwersje dynamiczne, w których powiązanie operacji jest zawieszone do czasu wykonywania. W czasie wykonywania wyszukiwane są niejawne konwersje typu czasu dwykonywania (string) do typu docelowego. Znaleziono konwersję, string ale nie do int.

przykład końcowy

10.2.11 Niejawne konwersje wyrażeń stałych

Niejawna konwersja wyrażenia stałego zezwala na następujące konwersje:

  • Typ constant_expression (§12.23) int można przekonwertować na typ sbyte, , byte, shortuintushort, lub ulong, pod warunkiem, że wartość constant_expression mieści się w zakresie typu docelowego.
  • Constant_expression typu long można przekonwertować na typ ulong, pod warunkiem że wartość constant_expression nie jest ujemna.

10.2.12 Niejawne konwersje obejmujące parametry typu

W przypadku type_parameterT, który jest znany jako typ odwołania (§15.2.5), istnieją następujące niejawne konwersje odwołań (§10.2.8):

  • Z T do efektywnej klasy Cbazowej , z T do dowolnej klasy bazowej C, i z T do dowolnego interfejsu zaimplementowanego przez C.
  • Od T do interface_type I w Tefektywnym zestawie interfejsów i z T do dowolnego podstawowego interfejsu I.
  • Od T do parametru U typu, który T zależy U od (§15.2.5).

    Uwaga: ponieważ T jest znany jako typ odwołania, w zakresie T, typ U czasu wykonywania zawsze będzie typem odwołania, nawet jeśli U nie jest znany jako typ odwołania w czasie kompilacji. notatka końcowa

  • Od literału null (§6.4.5.7) do T.

W przypadku type_parameterT, który nie jest znany jako typ odwołania §15.2.5, następujące konwersje obejmujące T są uważane za konwersje boksu (§10.2.9) w czasie kompilacji. W czasie wykonywania, jeśli T jest typem wartości, konwersja jest wykonywana jako konwersja boksu. W czasie wykonywania, jeśli T jest typem odwołania, konwersja jest wykonywana jako niejawna konwersja odwołania lub konwersja tożsamości.

  • Z T do efektywnej klasy Cbazowej , z T do dowolnej klasy bazowej C, i z T do dowolnego interfejsu zaimplementowanego przez C.

    Uwaga: C będzie jednym z typów System.Object, System.ValueTypelub System.Enum (w przeciwnym razie T jest znany jako typ odwołania). notatka końcowa

  • Od T do interface_type I w Tefektywnym zestawie interfejsów i z T do dowolnego podstawowego interfejsu I.

W przypadku type_parameterT, który nie jest znany jako typ odwołania, istnieje niejawna konwersja z T do podanego T parametru U typu zależy od U. W czasie wykonywania, jeśli T jest typem wartości i U jest typem referencyjnym, konwersja jest wykonywana jako konwersja boksu. W czasie wykonywania, jeśli zarówno T typy wartości, jak i U są, i T muszą być tego samego typu i U nie jest wykonywana żadna konwersja. W czasie wykonywania, jeśli T jest typem odwołania, jest to koniecznie również typ odwołania, U a konwersja jest wykonywana jako niejawna konwersja odwołania lub konwersja tożsamości (§15.2.5).

Dla danego parametru Ttypu istnieją następujące dalsze niejawne konwersje:

  • Od do typu S odwołania, jeśli ma niejawną konwersję na typ S₀ odwołania i S₀ ma konwersję tożsamości na S.T W czasie wykonywania konwersja jest wykonywana w taki sam sposób, jak konwersja na S₀.
  • Od T do typu I interfejsu, jeśli ma niejawną konwersję na typ I₀interfejsu , i I₀ jest wariancja-cabrio do I (§18.2.3.3). W czasie wykonywania, jeśli T jest typem wartości, konwersja jest wykonywana jako konwersja boksu. W przeciwnym razie konwersja jest wykonywana jako niejawna konwersja odwołania lub konwersja tożsamości.

We wszystkich przypadkach reguły zapewniają, że konwersja jest wykonywana jako konwersja boksu, jeśli i tylko wtedy, gdy w czasie wykonywania konwersja pochodzi z typu wartości do typu odwołania.

10.2.13 Niejawne konwersje krotki

Niejawna konwersja istnieje z wyrażenia E krotki na typ T krotki, jeśli E ma taką samą wartość, jak T i niejawna konwersja istnieje z każdego elementu w E do odpowiedniego typu elementu w .T Konwersja jest wykonywana przez utworzenie wystąpienia odpowiadającego TSystem.ValueTuple<...> mu typu i zainicjowanie każdego z jego pól w kolejności od lewej do prawej przez obliczenie odpowiedniego wyrażenia Eelementu krotki , przekonwertowanie go na odpowiedni typ T elementu przy użyciu znalezionej niejawnej konwersji i zainicjowanie pola z wynikiem.

Jeśli nazwa elementu w wyrażeniu krotki nie jest zgodna z odpowiednią nazwą elementu w typie krotki, zostanie wydane ostrzeżenie.

Przykład:

(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

Deklaracje t1, t2t4 i t5 są prawidłowe, ponieważ niejawne konwersje istnieją z wyrażeń elementów do odpowiednich typów elementów. Deklaracja elementu jest nieprawidłowa t3 , ponieważ nie ma konwersji z null na int. Deklaracja t5 powoduje ostrzeżenie, ponieważ nazwy elementów w wyrażeniu krotki różnią się od tych w typie krotki.

przykład końcowy

10.2.14 Niejawne konwersje zdefiniowane przez użytkownika

Niejawna konwersja zdefiniowana przez użytkownika składa się z opcjonalnej standardowej niejawnej konwersji, a następnie wykonywania operatora niejawnej zdefiniowanej przez użytkownika, a następnie innej opcjonalnej standardowej niejawnej konwersji. Dokładne reguły oceny niejawnych konwersji zdefiniowanych przez użytkownika opisano w §10.5.4.

10.2.15 Konwersje funkcji anonimowych i konwersje grup metod

Funkcje anonimowe i grupy metod nie mają typów samodzielnie, ale mogą być niejawnie konwertowane na typy delegatów. Ponadto niektóre wyrażenia lambda mogą być niejawnie konwertowane na typy drzewa wyrażeń. Konwersje funkcji anonimowych opisano bardziej szczegółowo w temacie §10.7 i konwersji grup metod w §10.8.

10.2.16 Domyślne konwersje literałów

Niejawna konwersja istnieje z default_literal (§12.8.21) do dowolnego typu. Ta konwersja generuje wartość domyślną (§9.3) wnioskowanego typu.

10.2.17 Niejawne konwersje rzutów

Chociaż wyrażenia throw nie mają typu, mogą być niejawnie konwertowane na dowolny typ.

10.3 Konwersje jawne

10.3.1 Ogólne

Następujące konwersje są klasyfikowane jako konwersje jawne:

  • Wszystkie niejawne konwersje (§10.2)
  • Jawne konwersje liczbowe (§10.3.2)
  • Jawne konwersje wyliczenia (§10.3.3)
  • Jawne konwersje dopuszczane do wartości null (§10.3.4)
  • Jawne konwersje krotki (§10.3.6)
  • Jawne konwersje odwołań (§10.3.5)
  • Jawne konwersje interfejsu
  • Konwersje rozpatłaniania (§10.3.7)
  • Jawne konwersje parametrów typu (§10.3.8)
  • Jawne konwersje zdefiniowane przez użytkownika (§10.3.9)

Jawne konwersje mogą wystąpić w wyrażeniach rzutowych (§12.9.7).

Zestaw jawnych konwersji obejmuje wszystkie konwersje niejawne.

Uwaga: na przykład umożliwia użycie jawnego rzutowania, gdy istnieje niejawna konwersja tożsamości, aby wymusić wybór określonego przeciążenia metody. notatka końcowa

Jawne konwersje, które nie są niejawnymi konwersjami, to konwersje, których nie można udowodnić, że zawsze się powiedzie, konwersje, które są znane jako możliwe do utraty informacji, i konwersje między domenami typów wystarczająco różne, aby uzyskać jawną notację.

10.3.2 Jawne konwersje liczbowe

Jawne konwersje liczbowe to konwersje z numeric_type do innego numeric_type , dla którego niejawna konwersja liczbowa (§10.2.3) jeszcze nie istnieje:

  • Z sbyte do byte, , uintushort, , ulonglub char.
  • Od byte do lub charsbyte .
  • Z short do sbyte, , byte, uintushort, , ulonglub char.
  • Z ushort do sbyte, byte, shortlub char.
  • Z int do sbyte, , shortuintulongbyteushortlub .char
  • Z uint do sbyte, , byte, ushortshort, , intlub char.
  • Z long do sbyte, , shortintushortuintbyte, ulonglub .char
  • Z ulong do sbyte, , shortintushortuintbyte, longlub .char
  • Z char do sbyte, bytelub short.
  • Z float do sbyte, byte, shortlongushortuintulongint, , charlub .decimal
  • Z double do sbyte, , shortintuintushortbyteulongcharlongfloatlub .decimal
  • Z decimal do sbyte, , shortintuintushortbyteulongcharlongfloatlub .double

Ponieważ jawne konwersje obejmują wszystkie niejawne i jawne konwersje liczbowe, zawsze można przekonwertować z dowolnego numeric_type na inne numeric_type przy użyciu wyrażenia rzutowania (§12.9.7).

Jawne konwersje liczbowe mogą utracić informacje lub spowodować zgłaszanie wyjątków. Jawna konwersja liczbowa jest przetwarzana w następujący sposób:

  • W przypadku konwersji typu całkowitego na inny typ całkowity przetwarzanie zależy od kontekstu sprawdzania przepełnienia (§12.8.20), w którym odbywa się konwersja:
    • checked W kontekście konwersja powiedzie się, jeśli wartość operandu źródłowego mieści się w zakresie typu docelowego, ale zgłasza System.OverflowException wartość, jeśli wartość operandu źródłowego znajduje się poza zakresem typu docelowego.
    • unchecked W kontekście konwersja zawsze się powiedzie i będzie kontynuowana w następujący sposób.
      • Jeśli typ źródła jest większy niż typ docelowy, wartość źródłowa jest obcięta przez odrzucenie jego "dodatkowych" najbardziej znaczących bitów. Wynik jest następnie traktowany jako wartość typu docelowego.
      • Jeśli typ źródła jest taki sam jak typ docelowy, wartość źródłowa jest traktowana jako wartość typu docelowego
  • W przypadku konwersji z decimal na typ całkowity wartość źródłowa jest zaokrąglona w kierunku zera do najbliższej wartości całkowitej, a ta wartość całkowita staje się wynikiem konwersji. Jeśli wynikowa wartość całkowita znajduje się poza zakresem typu docelowego, zwraca wartość .System.OverflowException
  • W przypadku konwersji z float lub double na typ całkowity przetwarzanie zależy od kontekstu sprawdzania przepełnienia (§12.8.20), w którym odbywa się konwersja:
    • W zaznaczonym kontekście konwersja jest kontynuowana w następujący sposób:
      • Jeśli wartość operandu to NaN lub nieskończona, System.OverflowException jest zwracana wartość .
      • W przeciwnym razie operand źródłowy jest zaokrąglany w kierunku zera do najbliższej wartości całkowitej. Jeśli ta wartość całkowita znajduje się w zakresie typu docelowego, ta wartość jest wynikiem konwersji.
      • W przeciwnym razie zgłaszany jest element System.OverflowException .
    • W nieznakowanym kontekście konwersja zawsze się powiedzie i będzie kontynuowana w następujący sposób.
      • Jeśli wartość operandu to NaN lub nieskończona, wynikiem konwersji jest nieokreślona wartość typu docelowego.
      • W przeciwnym razie operand źródłowy jest zaokrąglany w kierunku zera do najbliższej wartości całkowitej. Jeśli ta wartość całkowita znajduje się w zakresie typu docelowego, ta wartość jest wynikiem konwersji.
      • W przeciwnym razie wynik konwersji jest nieokreśloną wartością typu docelowego.
  • W przypadku konwersji z double na floatwartość double wartość jest zaokrąglona do najbliższej float wartości. double Jeśli wartość jest za mała, aby reprezentować jako floatwartość , wynik stanie się zerowy z tym samym znakiem co wartość. Jeśli wielkość double wartości jest zbyt duża, aby reprezentować jako floatwartość , wynik staje się nieskończoność z tym samym znakiem co wartość. double Jeśli wartość to NaN, wynik jest również wartością NaN.
  • W przypadku konwersji z lub na wartość źródłowa jest konwertowana na reprezentację i zaokrąglana do decimal najbliższej liczby, jeśli jest to wymagane (§8.3.8).decimaldouble float
    • Jeśli wartość źródłowa jest zbyt mała, aby reprezentować jako decimalwartość , wynik staje się zerowy, zachowując znak oryginalnej wartości, jeśli decimal obsługuje wartości ze znakiem zero.
    • Jeśli wielkość wartości źródłowej jest zbyt duża, aby reprezentować wartość typu , lub ta wartość jest nieskończoność, wynik jest nieskończoność, zachowując znak oryginalnej wartości, jeśli reprezentacja dziesiętna obsługuje niedociągnienia decimal; w przeciwnym razie zgłaszany jest wyjątek System.OverflowException.
    • Jeśli wartość źródłowa to NaN, wynik to NaN, jeśli reprezentacja dziesiętna obsługuje sieci NaNs; w przeciwnym razie zgłaszany jest wyjątek System.OverflowException.
  • W przypadku konwersji z decimal do lub float decimal doublewartość jest zaokrąglona do najbliższej double lub float wartości. Jeśli wielkość wartości źródłowej jest zbyt duża, aby reprezentować w typie docelowym lub ta wartość jest nieskończoność, wynik jest nieskończoność, zachowując znak oryginalnej wartości. Jeśli wartość źródłowa to NaN, wynik to NaN. Chociaż ta konwersja może utracić precyzję, nigdy nie powoduje zgłoszenia wyjątku.

Uwaga: decimal typ nie jest wymagany do obsługi wartości infiniacji ani wartości NaN, ale może to zrobić. Jego zakres może być mniejszy niż zakres float wartości i double, ale nie jest gwarantowany. W przypadku decimal reprezentacji bez wartości niedociągnięć lub wartości NaN i z zakresem mniejszym niż floatwynik konwersji z decimal na float albo double nigdy nie będzie nieskończoność lub NaN. notatka końcowa

10.3.3 Jawne konwersje wyliczenia

Jawne konwersje wyliczenia to:

  • Z sbyte, , shortushortintlongcharulonguintdoublebytefloatlub decimal do dowolnej enum_type.
  • Z dowolnego enum_type do sbyte, byte, intuintlongushortshortcharfloatulongdoublelub .decimal
  • Od dowolnego enum_type do innych enum_type.

Jawna konwersja wyliczenia między dwoma typami jest przetwarzana przez traktowanie wszystkich uczestniczących enum_type jako podstawowego typu tej enum_type, a następnie przeprowadzania niejawnej lub jawnej konwersji liczbowej między wynikowymi typami.

Przykład: Biorąc pod uwagę enum_type E z i bazowym typem int, konwersja z E na byte jest przetwarzana jako jawna konwersja liczbowa (§10.3.2) z do byte, a konwersja z byte int na E jest przetwarzana jako niejawna konwersja liczbowa (§10.2.3) z byte na .int przykład końcowy

10.3.4 Jawne konwersje dopuszczane do wartości null

Jawne konwersje dopuszczane do wartości null to konwersje dopuszczane do wartości null (§10.6.1) pochodzące z jawnych i niejawnych wstępnie zdefiniowanych konwersji.

10.3.5 Jawne konwersje odwołań

Jawne konwersje odwołań to:

  • Od obiektu do innych reference_type.
  • Z dowolnego class_type do dowolnego class_type S T , podana S jest klasą bazową .T
  • Z dowolnego class_type do żadnego interface_type TS , dostarczone S nie jest zapieczętowane i dostarczone S nie implementuje .T
  • Z dowolnego interface_type do dowolnego class_type S T , podany T nie jest zapieczętowany ani dostarczany T implementuje .S
  • Z dowolnego interface_type do dowolnego interface_typeS T , podany S nie pochodzi z .T
  • Z array_type S z typem Sᵢ elementu do array_type T z typem Tᵢelementu , pod warunkiem, że wszystkie następujące elementy są spełnione:
    • S i T różnią się tylko w typie elementu. Innymi słowy, S i T mają taką samą liczbę wymiarów.
    • Jawna konwersja odwołania istnieje z Sᵢ do Tᵢ.
  • Z System.Array poziomu interfejsów, które implementuje, do dowolnego array_type.
  • Z jednowymiarowej array_type S[] do , i jego interfejsów podstawowych, pod warunkiem, że istnieje konwersja tożsamości lub jawna konwersja odwołania z S do T. System.Collections.Generic.IReadOnlyList<T>System.Collections.Generic.IList<T>
  • Z System.Collections.Generic.IList<S>, System.Collections.Generic.IReadOnlyList<S>i ich interfejsy podstawowe do jednowymiarowego typu T[]tablicy , pod warunkiem, że istnieje konwersja tożsamości lub jawna konwersja odwołania z S do T.
  • Z System.Delegate interfejsów i implementuje do dowolnego delegate_type.
  • Od typu odwołania do typu S T odwołania, jeśli ma jawną konwersję odwołania z S do typu T₀ odwołania i T₀ istnieje konwersja tożsamości z T₀ na T.
  • Od typu S odwołania do interfejsu lub typu T delegata, jeśli istnieje jawna konwersja odwołania z S do interfejsu lub typu T₀ delegata i T₀ albo jest wariancja-cabrio do T lub T jest wariancja-cabrio do T₀ §18.2.3.3.
  • Od D<S₁...Sᵥ> lokalizacji, gdzie D<T₁...Tᵥ> D<X₁...Xᵥ> jest ogólnym typem delegata, D<S₁...Sᵥ> nie jest zgodny z lub identyczny D<T₁...Tᵥ>z parametrem , i dla każdego parametru Xᵢ typu następującego D blokady:
    • Jeśli Xᵢ zmienna jest niezmienna, Sᵢ wartość jest taka sama jak Tᵢ.
    • Jeśli Xᵢ jest kowariantny, istnieje konwersja tożsamości, niejawna konwersja odwołania lub jawna konwersja odwołania z Sᵢ do Tᵢ.
    • Jeśli Xᵢ jest kontrawariantny, to Sᵢ i Tᵢ są identyczne lub oba typy odwołań.
  • Jawne konwersje obejmujące parametry typu, które są znane jako typy odwołań. Aby uzyskać więcej informacji na temat jawnych konwersji obejmujących parametry typu, zobacz §10.3.8.

Jawne konwersje odwołań to konwersje między reference_types, które wymagają kontroli czasu wykonywania, aby upewnić się, że są poprawne.

Aby jawna konwersja referencyjna zakończyła się powodzeniem w czasie wykonywania, wartość operandu źródłowego wynosi null, lub typ obiektu, do którego odwołuje się operand źródłowy, jest typem, który można przekonwertować na typ docelowy przez niejawną konwersję odwołania (§10.2.8). Jeśli jawna konwersja odwołania zakończy się niepowodzeniem System.InvalidCastException , zostanie zgłoszony wyjątek .

Uwaga: Konwersje odwołań, niejawne lub jawne, nigdy nie zmieniają wartości samego odwołania (§8.2.1), tylko jego typu; ani nie zmienia typu ani wartości obiektu, do których odwołuje się odwołanie. notatka końcowa

10.3.6 Jawne konwersje krotki

Jawna konwersja istnieje z wyrażenia E krotki na typ T krotki, jeśli E ma taką samą wartość, jak T i niejawna lub jawna konwersja istnieje z każdego elementu w E obiekcie do odpowiedniego typu elementu w T. Konwersja jest wykonywana przez utworzenie wystąpienia odpowiadającego TSystem.ValueTuple<...> mu typu i zainicjowanie każdego z jego pól w kolejności od lewej do prawej przez obliczenie odpowiedniego wyrażenia Eelementu krotki , przekonwertowanie go na odpowiedni typ T elementu przy użyciu znalezionej jawnej konwersji i zainicjowanie pola z wynikiem.

10.3.7 Konwersje rozpatłaniania

Konwersja rozpakowania umożliwia jawną konwersję reference_type na value_type. Istnieją następujące konwersje rozpakowania:

  • Od typu object do dowolnego value_type.
  • Od typu System.ValueType do dowolnego value_type.
  • Od typu System.Enum do dowolnego enum_type.
  • Z dowolnego interface_type do dowolnego non_nullable_value_type, który implementuje interface_type.
  • Z dowolnego interface_type do dowolnego non_nullable_value_typeI, w którym istnieje konwersja rozpboxowania z interface_type do typu non_nullable_value I₀ i konwersja tożsamości z I na .I₀
  • Od dowolnego interface_type do dowolnego non_nullable_value_typeI, w którym istnieje konwersja rozpakowa z interface_type I₀ do non_nullable_value_type i I₀ albo jest variance_convertible do I lub I jest wariancja-kabriolet do I₀ (§18.2.3.3).
  • Z dowolnego reference_type do dowolnego nullable_value_type, w którym istnieje konwersja rozpboxowania z reference_type do bazowej non_nullable_value_type nullable_value_type.
  • Z parametru typu, który nie jest znany jako typ wartości do dowolnego typu, taki, że konwersja jest dozwolona przez §10.3.8.

Operacja rozpychania do non_nullable_value_type polega na pierwszym sprawdzeniu, czy wystąpienie obiektu jest wartością w polu danego non_nullable_value_type, a następnie skopiowaniem wartości z wystąpienia.

Odpakowywanie do nullable_value_type powoduje wygenerowanie wartości null nullable_value_type , jeśli operand źródłowy to null, lub otokowany wynik odłączenia wystąpienia obiektu do bazowego typu nullable_value_type w przeciwnym razie.

Uwaga: Odwołując się do wyimaginowanej klasy boksu opisanej w §10.2.9, konwersja pola obiektu na value_type S składa się z wykonywania wyrażenia ((S_Boxing)box).value. W związku z tym instrukcje

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

koncepcyjnie odpowiadają

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

notatka końcowa

W przypadku konwersji rozpychanej na daną non_nullable_value_type pomyślnej w czasie wykonywania wartość operandu źródłowego jest odwołaniem do wartości pola tej non_nullable_value_type. Jeśli argument argumentu źródłowego System.NullReferenceException jest null zgłaszany. Jeśli operand źródłowy jest odwołaniem do niezgodnego obiektu, System.InvalidCastException zgłaszany jest argument .

W przypadku konwersji rozpychania na daną nullable_value_type pomyślnej w czasie wykonywania wartość operandu źródłowego musi mieć wartość null lub odwołanie do wartości pola bazowej non_nullable_value_type nullable_value_type. Jeśli operand źródłowy jest odwołaniem do niezgodnego obiektu, System.InvalidCastException zgłaszany jest argument .

10.3.8 Jawne konwersje obejmujące parametry typu

Dla type_parameterT, który jest znany jako typ odwołania (§15.2.5), istnieją następujące jawne konwersje odwołań (§10.3.5):

  • Od efektywnej klasy C bazowej do T i z dowolnej klasy bazowej C T do Tklasy .
  • Z dowolnego interface_type do T.
  • Od T do dowolnego interface_type I pod warunkiem, że nie ma jeszcze niejawnej konwersji odwołania z T do I.
  • Od type_parameter U do T podanego, że T zależy od U (§15.2.5).

    Uwaga: ponieważ T jest znany jako typ odwołania, w zakresie T, typ czasu wykonywania zawsze będzie typem odwołania, nawet jeśli U nie jest znany jako typ odwołania w czasie kompilacji. notatka końcowa

W przypadku type_parameterT, który nie jest znany jako typ odwołania (§15.2.5), następujące konwersje obejmujące T są uważane za konwersje rozpałkania (§10.3.7) w czasie kompilacji. W czasie wykonywania, jeśli T jest typem wartości, konwersja jest wykonywana jako konwersja rozpakłania. W czasie wykonywania, jeśli T jest typem odwołania, konwersja jest wykonywana jako jawna konwersja odwołania lub konwersja tożsamości.

  • Od efektywnej klasy C bazowej do T i z dowolnej klasy bazowej C T do Tklasy .

    Uwaga: C będzie jednym z typów System.Object, System.ValueTypelub System.Enum (w przeciwnym razie T będzie znany jako typ odwołania). notatka końcowa

  • Z dowolnego interface_type do T.

W przypadku type_parameterT, który nie jest znany jako typ odwołania (§15.2.5), istnieją następujące jawne konwersje:

  • Od T do dowolnego interface_type I pod warunkiem, że nie ma jeszcze niejawnej konwersji z T do I. Ta konwersja składa się z niejawnej konwersji boksu (§10.2.9) z T do object , po której następuje jawna konwersja odwołania z object do I. W czasie wykonywania, jeśli T jest typem wartości, konwersja jest wykonywana jako konwersja boksu, po której następuje jawna konwersja odwołania. W czasie wykonywania, jeśli T jest typem odwołania, konwersja jest wykonywana jako jawna konwersja odwołania.
  • Od parametru U typu do T podanego, który T zależy U od (§15.2.5). W czasie wykonywania, jeśli T jest typem wartości i U jest typem referencyjnym, konwersja jest wykonywana jako konwersja rozpieczętowana. W czasie wykonywania, jeśli zarówno T typy wartości, jak i U są, i T muszą być tego samego typu i U nie jest wykonywana żadna konwersja. W czasie wykonywania, jeśli T jest typem odwołania, U musi być również typem odwołania, a konwersja jest wykonywana jako jawna konwersja referencyjna lub konwersja tożsamości.

We wszystkich przypadkach reguły zapewniają, że konwersja jest wykonywana jako konwersja rozpętywania, jeśli i tylko wtedy, gdy w czasie wykonywania konwersja pochodzi z typu odwołania do typu wartości.

Powyższe reguły nie zezwalają na bezpośrednią jawną konwersję z parametru typu bez ograniczeń do typu innego niż interfejs, co może być zaskakujące. Przyczyną tej reguły jest zapobieganie nieporozumieniu i czyszczenie semantyki takich konwersji.

Przykład: Rozważ następującą deklarację:

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

Jeśli dozwolona jest bezpośrednia jawna konwersja elementu t , long można łatwo oczekiwać, że X<int>.F(7) zwróci 7Lwartość . Jednak nie byłoby tak, ponieważ standardowe konwersje liczbowe są brane pod uwagę tylko wtedy, gdy typy są znane jako liczbowe w czasie powiązania. Aby semantyka było jasne, zamiast tego należy napisać powyższy przykład:

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

Ten kod zostanie teraz skompilowany, ale wykonanie X<int>.F(7) spowoduje zgłoszenie wyjątku w czasie wykonywania, ponieważ nie można przekonwertować pola int bezpośrednio na longelement .

przykład końcowy

10.3.9 Jawne konwersje zdefiniowane przez użytkownika

Jawna konwersja zdefiniowana przez użytkownika składa się z opcjonalnej standardowej jawnej konwersji, a następnie wykonywania niejawnego lub jawnego operatora konwersji zdefiniowanego przez użytkownika, a następnie innej opcjonalnej standardowej jawnej konwersji. Dokładne reguły oceny jawnych konwersji zdefiniowanych przez użytkownika opisano w §10.5.5.

Konwersje standardowe 10.4

10.4.1 Ogólne

Konwersje standardowe to wstępnie zdefiniowane konwersje, które mogą wystąpić w ramach konwersji zdefiniowanej przez użytkownika.

10.4.2 Standardowe konwersje niejawne

Następujące niejawne konwersje są klasyfikowane jako standardowe konwersje niejawne:

  • Konwersje tożsamości (§10.2.2)
  • Niejawne konwersje liczbowe (§10.2.3)
  • Niejawne konwersje dopuszczane do wartości null (§10.2.6)
  • Konwersje literału null (§10.2.7)
  • Niejawne konwersje odwołań (§10.2.8)
  • Konwersje boksu (§10.2.9)
  • Niejawne konwersje wyrażeń stałych (§10.2.11)
  • Niejawne konwersje obejmujące parametry typu (§10.2.12)

Standardowe konwersje niejawne wykluczają konwersje niejawne zdefiniowane przez użytkownika.

10.4.3 Standardowe konwersje jawne

Standardowe konwersje jawne to wszystkie standardowe konwersje niejawne oraz podzbiór jawnych konwersji, dla których istnieje odwrotna standardowa niejawna konwersja.

Uwaga: innymi słowy, jeśli istnieje standardowa niejawna konwersja typu na typ A B, standardowa jawna konwersja istnieje z typu na typ A B i od typu B do typu A. notatka końcowa

10.5 Konwersje zdefiniowane przez użytkownika

10.5.1 Ogólne

Język C# umożliwia wstępnie zdefiniowane niejawne i jawne konwersje rozszerzone przez konwersje zdefiniowane przez użytkownika. Konwersje zdefiniowane przez użytkownika są wprowadzane przez deklarowanie operatorów konwersji (§15.10.4) w typach klas i struktur.

10.5.2 Dozwolone konwersje zdefiniowane przez użytkownika

Język C# zezwala na deklarowanie tylko niektórych konwersji zdefiniowanych przez użytkownika. W szczególności nie można ponownie zdefiniować istniejącej niejawnej lub jawnej konwersji.

Dla danego typu źródłowego i typu S Tdocelowego , jeśli S lub T są typami wartości dopuszczania wartości null, pozwól S₀ i T₀ odwołaj się do ich typów bazowych, w przeciwnym razie S₀ i T₀ są równe S i T odpowiednio. Klasa lub struktura może zadeklarować konwersję z typu źródłowego na typ S T docelowy tylko wtedy, gdy spełnione są wszystkie następujące elementy:

  • S₀ i T₀ są różnymi typami.
  • Albo S₀ jest klasą lub T₀ typem struktury, w którym odbywa się deklaracja operatora.
  • Ani nie S₀ T₀ jest interface_type.
  • Wykluczanie konwersji zdefiniowanych przez użytkownika, konwersja nie istnieje z S do T lub z T do S.

Ograniczenia dotyczące konwersji zdefiniowanych przez użytkownika są określone w §15.10.4.

10.5.3 Ocena konwersji zdefiniowanych przez użytkownika

Konwersja zdefiniowana przez użytkownika konwertuje wyrażenie źródłowe, które może mieć typ źródłowy, na inny typ nazywany typem docelowym. Ocena zdefiniowanego przez użytkownika centrum konwersji w celu znalezienia najbardziej określonego operatora konwersji zdefiniowanego przez użytkownika dla wyrażenia źródłowego i typu docelowego. Ta determinacja jest podzielona na kilka kroków:

  • Znajdowanie zestawu klas i struktur, z których będą brane pod uwagę operatory konwersji zdefiniowane przez użytkownika. Ten zestaw składa się z typu źródłowego i jego klas bazowych, jeśli typ źródłowy istnieje, wraz z typem docelowym i jego klasami podstawowymi. W tym celu zakłada się, że tylko klasy i struktury mogą deklarować operatory zdefiniowane przez użytkownika, a typy nieklasowe nie mają klas bazowych. Ponadto jeśli typ źródłowy lub docelowy jest typem wartości null, używany jest ich typ bazowy.
  • Z tego zestawu typów określ, które operatory konwersji zdefiniowane przez użytkownika i zniesione mają zastosowanie. Aby operator konwersji mógł mieć zastosowanie, możliwe jest przeprowadzenie konwersji standardowej (§10.4) od wyrażenia źródłowego do typu operand operatora i możliwe jest przeprowadzenie standardowej konwersji z typu wyniku operatora na typ docelowy.
  • Z zestawu odpowiednich operatorów zdefiniowanych przez użytkownika, określając, który operator jest jednoznacznie najbardziej specyficzny. Ogólnie rzecz biorąc, najbardziej specyficznym operatorem jest operator, którego typ operandu jest "najbliżej" wyrażenia źródłowego i którego typ wyniku jest "najbliżej" typu docelowego. Operatory konwersji zdefiniowane przez użytkownika są preferowane przez operatory konwersji zniesionej. Dokładne reguły ustanawiania najbardziej specyficznego operatora konwersji zdefiniowanego przez użytkownika są zdefiniowane w następujących podklasach.

Po zidentyfikowaniu najbardziej określonego operatora konwersji zdefiniowanego przez użytkownika rzeczywiste wykonanie konwersji zdefiniowanej przez użytkownika obejmuje maksymalnie trzy kroki:

  • Najpierw, jeśli jest to wymagane, wykonanie standardowej konwersji z wyrażenia źródłowego na typ operandu zdefiniowanego przez użytkownika lub operatora konwersji zniesionej.
  • Następnie wywołanie zdefiniowanego przez użytkownika lub zniesionego operatora konwersji w celu przeprowadzenia konwersji.
  • Na koniec, jeśli jest to wymagane, wykonanie standardowej konwersji z typu wyniku operatora konwersji zdefiniowanego przez użytkownika na typ docelowy.

Ocena konwersji zdefiniowanej przez użytkownika nigdy nie obejmuje więcej niż jednego operatora konwersji zdefiniowanego przez użytkownika lub zniesionego. Innymi słowy konwersja typu na typ S T nigdy nie będzie najpierw wykonywać konwersji zdefiniowanej przez użytkownika z S do X , a następnie wykonać konwersję zdefiniowaną przez użytkownika z X na T.

  • Dokładne definicje oceny niejawnych lub jawnych konwersji zdefiniowanych przez użytkownika są podane w następujących podklasach. Definicje korzystają z następujących terminów:
  • Jeśli standardowa niejawna konwersja (§10.4.2) istnieje z typu na typ A B, a jeśli ani nie A Binterface_type s , mówi się, że jest uwzględniana przez B, i B mówi się, A że obejmuje .A
  • Jeśli standardowa niejawna konwersja (§10.4.2) istnieje z wyrażenia E na typ B, a jeśli ani typ B E (jeśli go ma) nie są interface_type s, E mówi się, że zostanie objęteB, i B mówi się, że obejmujeE.
  • Najbardziej obejmującym typem zestawu typów jest jeden typ, który obejmuje wszystkie inne typy w zestawie. Jeśli żaden pojedynczy typ nie obejmuje wszystkich innych typów, zestaw nie ma najbardziej obejmującego typu. W bardziej intuicyjnych kategoriach najbardziej obejmujący typ to "największy" typ zestawu — jeden typ, do którego można niejawnie konwertować poszczególne typy.
  • Najbardziej obejmującym typ zestawu typów jest jednym typem obejmującym wszystkie inne typy w zestawie. Jeśli żaden pojedynczy typ nie jest uwzględniany przez wszystkie inne typy, zestaw nie ma najbardziej objętego typu. W bardziej intuicyjnych kategoriach najbardziej obejmujący typ jest "najmniejszym" typem w zestawie — jednym typem, który może być niejawnie konwertowany na każdy z innych typów.

10.5.4 Niejawne konwersje zdefiniowane przez użytkownika

Zdefiniowana przez użytkownika niejawna konwersja z wyrażenia E na typ T jest przetwarzana w następujący sposób:

  • Określanie typów S, S₀ i T₀.

    • Jeśli E ma typ, niech S będzie to typ.
    • Jeśli S lub T są typami wartości dopuszczania wartości null, niech Sᵢ i Tᵢ będą ich podstawowymi typami, w przeciwnym razie let Sᵢ i Tᵢ be S i T, odpowiednio.
    • Jeśli Sᵢ lub Tᵢ są parametrami typu, let i be ich efektywne klasy bazowe, w przeciwnym razie let S₀ S₀ i T₀ be Sₓ i T₀ Tᵢ, odpowiednio.
  • Znajdź zestaw typów , Dz którego zostaną uwzględnione operatory konwersji zdefiniowane przez użytkownika. Ten zestaw składa się z S₀ (jeśli S₀ istnieje i jest klasą lub strukturą), klas bazowych S₀ (jeśli S₀ istnieje i jest klasą) oraz T₀ (jeśli T₀ jest klasą lub strukturą). Typ jest dodawany do zestawu D tylko wtedy, gdy konwersja tożsamości na inny typ już uwzględniony w zestawie nie istnieje.

  • Znajdź zestaw odpowiednich operatorów konwersji zdefiniowanych przez użytkownika i zniesionych, U. Ten zestaw składa się z zdefiniowanych przez użytkownika i podniesionych niejawnych operatorów konwersji zadeklarowanych przez klasy lub struktury w D tej konwersji z typu obejmującego typ obejmujący typ obejmujący E przez Telement . Jeśli U jest pusty, konwersja jest niezdefiniowana i występuje błąd czasu kompilacji.

    • Jeśli S istnieje i którykolwiek z operatorów w U konwersji z S, to Sₓ jest S.
    • Sₓ W przeciwnym razie jest najbardziej obejmującym typem w połączonym zestawie typów źródłowych operatorów w programie U. Jeśli nie można odnaleźć dokładnie jednego najbardziej objętego typu, konwersja jest niejednoznaczna i wystąpi błąd czasu kompilacji.
  • Znajdź najbardziej specyficzny typ docelowy, Tₓ, operatorów w pliku U:

    • Jeśli którykolwiek z operatorów w U konwersji na T, to Tₓ ma wartość T.
    • Tₓ W przeciwnym razie jest najbardziej obejmującym typem w połączonym zestawie typów docelowych operatorów w systemie U. Jeśli nie można odnaleźć dokładnie jednego typu obejmującego, konwersja jest niejednoznaczna i występuje błąd czasu kompilacji.
  • Znajdź najbardziej specyficzny operator konwersji:

    • Jeśli U zawiera dokładnie jeden operator konwersji zdefiniowany przez użytkownika, który konwertuje Sₓ z na Tₓ, jest to najbardziej specyficzny operator konwersji.
    • W przeciwnym razie, jeśli U zawiera dokładnie jeden operator konwersji zniesionej, który konwertuje z Sₓ na Tₓ, jest to najbardziej specyficzny operator konwersji.
    • W przeciwnym razie konwersja jest niejednoznaczna i występuje błąd czasu kompilacji.
  • Na koniec zastosuj konwersję:

    • Jeśli E nie ma jeszcze typu Sₓ, wykonywana jest standardowa niejawna konwersja z E do Sₓ .
    • Najbardziej specyficzny operator konwersji jest wywoływany w celu konwersji z Sₓ na Tₓ.
    • Jeśli Tₓ nie Tma wartości , wykonywana jest standardowa niejawna konwersja z Tₓ do T .

Zdefiniowana przez użytkownika niejawna konwersja z typu S na typ T istnieje, jeśli zdefiniowana przez użytkownika niejawna konwersja istnieje ze zmiennej typu S na T.

10.5.5 Jawne konwersje zdefiniowane przez użytkownika

Zdefiniowana przez użytkownika jawna konwersja z wyrażenia E na typ T jest przetwarzana w następujący sposób:

  • Określanie typów S, S₀ i T₀.
    • Jeśli E ma typ, niech S będzie to typ.
    • Jeśli S lub T są typami wartości dopuszczania wartości null, niech Sᵢ i Tᵢ będą ich podstawowymi typami, w przeciwnym razie let Sᵢ i Tᵢ be S i T, odpowiednio.
    • Jeśli Sᵢ lub Tᵢ są parametrami typu, let i be ich efektywne klasy bazowe, w przeciwnym razie let S₀ S₀ i T₀ be Sᵢ i T₀ Tᵢ, odpowiednio.
  • Znajdź zestaw typów , Dz którego zostaną uwzględnione operatory konwersji zdefiniowane przez użytkownika. Ten zestaw składa się z S₀ (jeśli S₀ istnieje i jest klasą lub strukturą), klas bazowych S₀ (jeśli S₀ istnieje i jest klasą), T₀ (jeśli T₀ jest klasą lub strukturą) oraz klasami T₀ bazowymi (jeśli T₀ jest klasą). A Typ jest dodawany do zestawu D tylko wtedy, gdy konwersja tożsamości na inny typ już uwzględniony w zestawie nie istnieje.
  • Znajdź zestaw odpowiednich operatorów konwersji zdefiniowanych przez użytkownika i zniesionych, U. Ten zestaw składa się z zdefiniowanych przez użytkownika i podniesionych niejawnych lub jawnych operatorów konwersji zadeklarowanych przez klasy lub struktury w D tej konwersji z typu obejmującego lub S objętego E (jeśli istnieje) do typu obejmującego lub objętego przez Tprogram . Jeśli U jest pusty, konwersja jest niezdefiniowana i występuje błąd czasu kompilacji.
  • Znajdź najbardziej specyficzny typ źródła, Sₓ, operatorów w pliku U:
    • Jeśli S istnieje i którykolwiek z operatorów w U konwersji z S, to Sₓ jest S.
    • W przeciwnym razie, jeśli którykolwiek z operatorów w U konwersji z typów, które obejmują E, Sₓ jest najbardziej obejmującym typem w połączonym zestawie typów źródłowych tych operatorów. Jeśli nie można odnaleźć najbardziej objętego typu, konwersja jest niejednoznaczna i wystąpi błąd czasu kompilacji.
    • Sₓ W przeciwnym razie jest najbardziej obejmującym typem w połączonym zestawie typów źródłowych operatorów w systemie U. Jeśli nie można odnaleźć dokładnie jednego typu obejmującego, konwersja jest niejednoznaczna i występuje błąd czasu kompilacji.
  • Znajdź najbardziej specyficzny typ docelowy, Tₓ, operatorów w pliku U:
    • Jeśli którykolwiek z operatorów w U konwersji na T, to Tₓ ma wartość T.
    • W przeciwnym razie, jeśli którykolwiek z operatorów w U konwersji na typy, które są objęte przez T, Tₓ jest najbardziej obejmującym typem w połączonym zestawie typów docelowych tych operatorów. Jeśli nie można odnaleźć dokładnie jednego typu obejmującego, konwersja jest niejednoznaczna i występuje błąd czasu kompilacji.
    • Tₓ W przeciwnym razie jest najbardziej obejmującym typem w połączonym zestawie typów docelowych operatorów w systemie U. Jeśli nie można odnaleźć najbardziej objętego typu, konwersja jest niejednoznaczna i wystąpi błąd czasu kompilacji.
  • Znajdź najbardziej specyficzny operator konwersji:
    • Jeśli U zawiera dokładnie jednego operatora konwersji zdefiniowanego przez użytkownika, który konwertuje Sₓ z na Tₓ, jest to najbardziej specyficzny operator konwersji.
    • W przeciwnym razie, jeśli U zawiera dokładnie jeden operator konwersji zniesionej, który konwertuje z Sₓ na Tₓ, jest to najbardziej specyficzny operator konwersji.
    • W przeciwnym razie konwersja jest niejednoznaczna i występuje błąd czasu kompilacji.
  • Na koniec zastosuj konwersję:
    • Jeśli E nie ma jeszcze typu Sₓ, wykonywana jest standardowa jawna konwersja z E na Sₓ .
    • Najbardziej specyficzny operator konwersji zdefiniowany przez użytkownika jest wywoływany w celu konwersji z Sₓ na Tₓ.
    • Jeśli Tₓ nie Tma wartości , wykonywana jest standardowa jawna konwersja z Tₓ do T .

Jawna konwersja zdefiniowana przez użytkownika z typu na typ S T istnieje, jeśli zdefiniowana przez użytkownika jawna konwersja istnieje ze zmiennej typu S na T.

10.6 Konwersje obejmujące typy dopuszczane do wartości null

Konwersje dopuszczane do wartości null 10.6.1

Konwersje dopuszczające wartość null umożliwiają wstępnie zdefiniowane konwersje , które działają na typach wartości innych niż null, które mają być również używane z formami dopuszczanymi wartości null tych typów. Dla każdej wstępnie zdefiniowanej niejawnej lub jawnej konwersji, która konwertuje z typu wartości niepustej na typ S T wartości niepustej (§10.2.2, §10.2.3, §10.2.4, §10.2.11, §10.3.2 i §10.3.3), istnieją następujące konwersje dopuszczające wartość null:

  • Niejawna lub jawna konwersja z S? do T?
  • Niejawna lub jawna konwersja z S do T?
  • Jawna konwersja z S? na T.

Konwersja dopuszczana do wartości null jest klasyfikowana jako niejawna lub jawna konwersja.

Niektóre konwersje dopuszczane do wartości null są klasyfikowane jako konwersje standardowe i mogą wystąpić w ramach konwersji zdefiniowanej przez użytkownika. W szczególności wszystkie niejawne konwersje dopuszczające wartość null są klasyfikowane jako standardowe konwersje niejawne (§10.4.2), a te jawne konwersje dopuszczające wartość null spełniające wymagania §10.4.3 są klasyfikowane jako standardowe konwersje jawne.

Ocena konwersji dopuszczanej do wartości null na podstawie konwersji bazowej z S do T następuje w następujący sposób:

  • Jeśli konwersja dopuszczana do wartości null jest z S? do T?:
    • Jeśli wartość źródłowa ma wartość null (HasValue właściwość to false), wynikiem jest wartość null typu T?.
    • W przeciwnym razie konwersja jest obliczana jako rozpasanie z S? do S, a następnie konwersja bazowa z S na T, a następnie zawijanie z T do T?.
  • Jeśli konwersja dopuszczana do wartości null pochodzi z S do , konwersja jest obliczana jako konwersja bazowa z S do, po której następuje zawijanie z T do T T?.T?
  • Jeśli konwersja dopuszczana do wartości null wynosi od S? do , konwersja jest obliczana jako rozpasanie z S? do, po której następuje konwersja bazowa z S na S T.T

10.6.2 Konwersje zniesione

Biorąc pod uwagę zdefiniowany przez użytkownika operator konwersji, który konwertuje typ wartości S innej niż null na typ Twartości innej niż null, istnieje operator konwersji zniesionej, który konwertuje wartość z S? na T?. Ten operator zniesionej konwersji wykonuje odpisanie z S? doS, po którym następuje konwersja zdefiniowana przez użytkownika z S na, po której następuje zawijanie z T do T?T , z tą różnicą, że wartość S? null konwertuje bezpośrednio na wartość null.T? Operator konwersji zniesionej ma taką samą niejawną lub jawną klasyfikację jak jego podstawowy operator konwersji zdefiniowany przez użytkownika.

Konwersje funkcji anonimowych 10.7

10.7.1 Ogólne

Anonymous_method_expression lub lambda_expression jest klasyfikowana jako funkcja anonimowa (§12.19). Wyrażenie nie ma typu, ale może być niejawnie konwertowane na zgodny typ delegata. Niektóre wyrażenia lambda mogą być również niejawnie konwertowane na zgodny typ drzewa wyrażeń.

W szczególności funkcja F anonimowa jest zgodna z typem D delegata podanym:

  • Jeśli F zawiera anonymous_function_signature, to D i F mają taką samą liczbę parametrów.
  • Jeśli F nie zawiera anonymous_function_signature, D może mieć zero lub więcej parametrów dowolnego typu, o ile żaden parametr nie jest parametrem D wyjściowym.
  • Jeśli F ma jawnie wpisaną listę parametrów, każdy parametr w programie D ma te same modyfikatory co odpowiedni parametr w F programie, a konwersja tożsamości istnieje między odpowiednim parametrem w Fpliku .
  • Jeśli F ma niejawnie typizowane listy parametrów, D nie ma żadnych parametrów referencyjnych ani wyjściowych.
  • Jeśli treść F jest wyrażeniem, aD albo ma typ zwracany void lubF jest asynchroniczny i D ma «TaskType» typ zwracany (§15.15.1), kiedy każdy parametr F ma typ odpowiedniego parametru w D, treść F jest prawidłowym wyrażeniem (w.r.t §12), które byłoby dozwolone jako statement_expression (§13.7).
  • Jeśli treść obiektu jest blokiem iD ma typ zwracany void lubF jest asynchroniczny i D ma typ zwracany «TaskType» , wówczas, gdy każdy parametr ma typ odpowiedniego parametru F w D, treść F F jest prawidłowym blokiem (w.r.t §13.3), w którym żadna instrukcja nie return określa wyrażenia.
  • Jeśli treść F jest wyrażeniem, a alboF nie jest asynchroniczne i D mavoid typ niewzrótny T, lubF jest asynchroniczny i D ma «TaskType»<T> typ zwracany (§15.15.1), to gdy każdy parametr F ma typ odpowiadającego parametru w D, treść F jest prawidłowym wyrażeniem (w.r.t §12), które jest niejawnie konwertowane na T.
  • Jeśli treść obiektu jest blokiem iF albo nie jest asynchroniczny i D ma niepusty typ Tzwracany , lubF jest asynchroniczny i D ma typ zwracany, gdy każdy parametr F jest podany typ odpowiedniego parametru w D, treść F F jest prawidłowym blokiem instrukcji (w.r.t §13.3) z nieosiągalnym «TaskType»<T> punktem końcowym, w którym każda instrukcja return określa wyrażenie, które jest niejawnie konwertowane na .T

Przykład: Następujące przykłady ilustrują następujące reguły:

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

przykład końcowy

Przykład: Przykłady, które korzystają z ogólnego typu Func<A,R> delegata, który reprezentuje funkcję, która przyjmuje argument typu A i zwraca wartość typu R:

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

W przydziałach

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 i zwracane typy każdej funkcji anonimowej są określane z typu zmiennej, do której przypisano funkcję anonimową.

Pierwsze przypisanie pomyślnie konwertuje funkcję anonimową na typ Func<int,int> delegata, ponieważ w przypadku x danego typu intx + 1 jest prawidłowe wyrażenie niejawnie konwertowane na typ int.

Podobnie drugie przypisanie pomyślnie konwertuje funkcję anonimową na typ delegata Func<int,dwukrotnie> , ponieważ wynik x + 1 (typu int) jest niejawnie konwertowany na typ double.

Jednak trzecie przypisanie jest błędem czasu kompilacji, ponieważ w przypadku x danego typu doublewynik x + 1 (typu double) nie jest niejawnie konwertowany na typ int.

Czwarte przypisanie pomyślnie konwertuje anonimową funkcję asynchronicznie na typ Func<int, Task<int>> delegata, ponieważ wynik x + 1 (typu int) jest niejawnie konwertowany na efektywny typ int zwracany asynchronicznego lambda, który ma typ Task<int>zwracany .

przykład końcowy

Wyrażenie F lambda jest zgodne z typem Expression<D> drzewa wyrażeń, jeśli F jest zgodne z typem Ddelegata . Nie dotyczy to metod anonimowych, tylko wyrażeń lambda.

Funkcje anonimowe mogą mieć wpływ na rozpoznawanie przeciążenia i uczestniczyć w wnioskowaniu typu. Aby uzyskać więcej informacji, zobacz §12.6 .

10.7.2 Ocena konwersji funkcji anonimowych na typy delegatów

Konwersja funkcji anonimowej na typ delegata powoduje wystąpienie delegata, które odwołuje się do funkcji anonimowej i (prawdopodobnie puste) zestawu przechwyconych zmiennych zewnętrznych, które są aktywne w czasie oceny. Po wywołaniu delegata jest wykonywana treść funkcji anonimowej. Kod w treści jest wykonywany przy użyciu zestawu przechwyconych zmiennych zewnętrznych, do których odwołuje się delegat. Można użyć delegate_creation_expression (§12.8.17.6) jako alternatywnej składni do konwertowania metody anonimowej na typ delegata.

Lista wywołań delegata wygenerowanego z funkcji anonimowej zawiera pojedynczy wpis. Dokładny obiekt docelowy i metoda docelowa delegata nie są określone. W szczególności nie określono, czy obiekt docelowy delegata to null, this wartość otaczającego elementu członkowskiego funkcji lub inny obiekt.

Konwersje semantycznie identycznych funkcji anonimowych z tym samym (prawdopodobnie pustym) zestawem przechwyconych wystąpień zmiennych zewnętrznych do tych samych typów delegatów są dozwolone (ale nie są wymagane) do zwrócenia tego samego wystąpienia delegata. Termin semantycznie identyczny jest używany w tym miejscu, aby oznaczać, że wykonanie funkcji anonimowych w każdym przypadku spowoduje wygenerowanie tych samych efektów, biorąc pod uwagę te same argumenty. Ta reguła zezwala na optymalizację kodu, takiego jak poniższe.

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

Ponieważ dwa delegaty funkcji anonimowej mają ten sam (pusty) zestaw przechwyconych zmiennych zewnętrznych, a ponieważ funkcje anonimowe są semantycznie identyczne, kompilator może mieć delegaty odwoływać się do tej samej metody docelowej. W rzeczywistości kompilator może zwrócić to samo wystąpienie delegata z obu anonimowych wyrażeń funkcji.

10.7.3 Ocena konwersji wyrażeń lambda na typy drzewa wyrażeń

Konwersja wyrażenia lambda na typ drzewa wyrażeń powoduje utworzenie drzewa wyrażeń (§8.6). Dokładniej mówiąc, ocena konwersji wyrażenia lambda generuje strukturę obiektu reprezentującą strukturę samego wyrażenia lambda.

Nie każde wyrażenie lambda można przekonwertować na typy drzewa wyrażeń. Konwersja na zgodny typ delegata zawsze istnieje, ale może zakończyć się niepowodzeniem w czasie kompilacji ze względów zdefiniowanych przez implementację.

Uwaga: typowe przyczyny niepowodzenia konwersji wyrażenia lambda na typ drzewa wyrażeń obejmują:

  • Ma ciało bloku
  • Ma async modyfikator
  • Zawiera operator przypisania
  • Zawiera on parametr wyjściowy lub referencyjny
  • Zawiera wyrażenie dynamicznie powiązane

notatka końcowa

Konwersje grup metod 10.8

Niejawna konwersja istnieje z grupy metod (§12.2) do zgodnego typu delegata (§20.4). Jeśli D jest typem delegata i E jest wyrażeniem, które jest klasyfikowane jako grupa metod, jest D zgodne z if i tylko wtedy, gdy E zawiera co najmniej jedną metodę, która ma zastosowanie w postaci normalnej (§12.6.4.2) do dowolnej listy argumentów (§12.6.2) o typach i modyfikatorach pasujących do typów parametrów i modyfikatorów D, zgodnie z E opisem w poniższym artykule.

Zastosowanie konwersji w czasie kompilacji z grupy E metod do typu D delegata zostało opisane w poniższym artykule.

  • Wybrana jest pojedyncza metoda odpowiadająca wywołaniu metody M (§12.8.10.2) formularza E(A)z następującymi modyfikacjami:
    • Lista A argumentów jest listą wyrażeń, z których każda jest klasyfikowana jako zmienna i z typem i modyfikatorem (in, outlub ref) odpowiedniego parametru w parameter_list D — z wyjątkiem parametrów typu dynamic, gdzie odpowiadające wyrażenie ma typ object zamiast dynamic.
    • Rozważane metody kandydata to tylko te metody, które mają zastosowanie w ich normalnej postaci i nie pomijają żadnych parametrów opcjonalnych (§12.6.4.2). W związku z tym metody kandydatów są ignorowane, jeśli mają zastosowanie tylko w rozszerzonym formularzu, lub jeśli co najmniej jeden z parametrów opcjonalnych nie ma odpowiedniego parametru w pliku D.
  • Konwersja jest uważana za istniejącą, jeśli algorytm §12.8.10.2 tworzy jedną najlepszą metodę M zgodną (§20.4) z D.
  • Jeśli wybrana metoda M jest metodą wystąpienia, wyrażenie wystąpienia skojarzone z E określa obiekt docelowy delegata.
  • Jeśli wybrana metoda M jest metodą rozszerzenia, która jest oznaczona za pomocą dostępu do elementu członkowskiego w wyrażeniu wystąpienia, to wyrażenie wystąpienia określa obiekt docelowy delegata.
  • Wynikiem konwersji jest wartość typu D, a mianowicie delegat odwołujący się do wybranej metody i obiektu docelowego.

Przykład: Poniżej przedstawiono konwersje grup 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
    }
}

Przypisanie niejawnie d1 konwertuje grupę F metod na wartość typu D1.

Przypisanie d2 pokazuje, w jaki sposób można utworzyć delegata do metody, która ma mniej pochodne (kontrawariantne) typy parametrów i bardziej pochodny (kowariantny) typ zwracany.

Przypisanie pokazuje d3 , jak nie istnieje konwersja, jeśli metoda nie ma zastosowania.

Przypisanie, aby pokazać d4 , jak metoda musi być stosowana w postaci normalnej.

Przypisanie w celu pokazania d5 , jak typy parametrów i zwracanych delegatów i metod mogą się różnić tylko dla typów odwołań.

przykład końcowy

Podobnie jak w przypadku wszystkich innych niejawnych i jawnych konwersji, operator rzutowania może służyć do jawnego wykonania określonej konwersji.

Przykład: W związku z tym przykład

object obj = new EventHandler(myDialog.OkClick);

zamiast tego można napisać

object obj = (EventHandler)myDialog.OkClick;

przykład końcowy

Konwersja grupy metod może odwoływać się do metody ogólnej, jawnie określając argumenty typu w obiekcie Elub za pośrednictwem wnioskowania typu (§12.6.3). Jeśli jest używana wnioskowanie typu, typy parametrów delegata są używane jako typy argumentów w procesie wnioskowania. Zwracany typ delegata nie jest używany do wnioskowania. Niezależnie od tego, czy argumenty typu są określone, czy wnioskowane, są częścią procesu konwersji grupy metod; są to argumenty typu używane do wywoływania metody docelowej podczas wywoływania wynikowego delegata.

Przykład:

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

przykład końcowy

Grupy metod mogą mieć wpływ na rozpoznawanie przeciążenia i uczestniczyć w wnioskowaniu typów. Aby uzyskać więcej informacji, zobacz §12.6 .

Ocena czasu wykonywania konwersji grupy metod jest kontynuowana w następujący sposób:

  • Jeśli metoda wybrana w czasie kompilacji jest metodą wystąpienia lub jest metodą rozszerzenia, która jest dostępna jako metoda wystąpienia, obiekt docelowy delegata jest określany na podstawie wyrażenia wystąpienia skojarzonego z E:
    • Wyrażenie wystąpienia jest obliczane. Jeśli ta ocena powoduje wyjątek, nie są wykonywane żadne dalsze kroki.
    • Jeśli wyrażenie wystąpienia jest reference_type, wartość obliczona przez wyrażenie wystąpienia staje się obiektem docelowym. Jeśli wybrana metoda jest metodą wystąpienia, a obiektem docelowym jest null, System.NullReferenceException jest zgłaszany i nie są wykonywane żadne dalsze kroki.
    • Jeśli wyrażenie wystąpienia jest value_type, operacja boksu (§10.2.9) jest wykonywana w celu przekonwertowania wartości na obiekt, a ten obiekt staje się obiektem docelowym.
  • W przeciwnym razie wybrana metoda jest częścią wywołania metody statycznej, a obiektem docelowym delegata jest null.
  • Wystąpienie delegata typu D delegata jest uzyskiwane przy użyciu odwołania do metody określonej w czasie kompilacji i odwołania do obiektu docelowego obliczonego powyżej w następujący sposób:
    • Konwersja jest dozwolona (ale nie jest wymagana) do użycia istniejącego wystąpienia delegata, które zawiera już te odwołania.
    • Jeśli istniejące wystąpienie nie zostało ponownie użyte, zostanie utworzony nowy (§20.5). Jeśli nie ma wystarczającej ilości pamięci do przydzielenia nowego wystąpienia, System.OutOfMemoryException zgłaszany jest błąd . W przeciwnym razie wystąpienie jest inicjowane przy użyciu podanych odwołań.