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 typuint
mogą być niejawnie traktowane jako typlong
. Odwrotna konwersja, od typu do typulong
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
iT
dla dowolnego typuT
. - Między
T
iT?
dla dowolnego typuT
odwołania . - Między
object
idynamic
. - 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
t1
it3
t2
wszystkie mają dwa elementy: point
string
nim element . Typy elementów krotki mogą się składać z krotki, tak jak wt4
elementach ,t5
it6
. 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 krotkit4
,t5
it6
.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
doshort
, ,int
,float
long
, ,double
lubdecimal
. - Z
byte
doshort
, ,int
ulong
ushort
long
float
uint
double
lub .decimal
- Z
short
doint
, ,float
long
, ,double
lubdecimal
. - Z
ushort
doint
, ,long
float
double
uint
ulong
lub .decimal
- Z
int
dolong
,float
,double
lubdecimal
. - Z
uint
dolong
, ,float
ulong
, ,double
lubdecimal
. - Z
long
dofloat
,double
lubdecimal
. - Z
ulong
dofloat
,double
lubdecimal
. - Z
char
doushort
, ,uint
ulong
long
float
int
,double
lub .decimal
- Od
float
dodouble
.
Konwersje z int
, lub long
uint
ulong
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
idynamic
. - Z dowolnego class_type do dowolnego class_type
S
T
, podanyS
jest pochodzi z .T
- Z dowolnego class_type do dowolnego interface_type
S
T
, dostarczoneS
implementuje .T
- Z dowolnego interface_type do dowolnego interface_type
S
T
, podanyS
jest pochodzi z .T
- Z array_type
S
z typemSᵢ
elementu do array_typeT
z typemTᵢ
elementu , pod warunkiem, że wszystkie następujące elementy są spełnione:S
iT
różnią się tylko w typie elementu. Innymi słowy,S
iT
mają taką samą liczbę wymiarów.- Niejawna konwersja odwołania istnieje z
Sᵢ
doTᵢ
.
- Z jednowymiarowego typu
S[]
tablicy doSystem.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 zS
doT
. - 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_type
T
, jeśli ma niejawną tożsamość lub konwersję odwołania do reference_typeT₀
iT₀
ma konwersję tożsamości naT
. - Z dowolnego reference_type do interfejsu lub typu
T
delegata, jeśli ma niejawną tożsamość lub konwersję odwołania do interfejsu lub typuT₀
delegata iT₀
jest wariancja-cabrio (§18.2.3.3) doT
. - 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_typeI₀
iI₀
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_typeI₀
iI₀
jest wariancja-cabrio (§18.2.3.3) doI
. - 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 interfejsuI
z klasą boksu o nazwieS_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
typuS
składa się teraz z wykonywania wyrażenianew S_Boxing(v)
i zwracania wynikowego wystąpienia jako wartości typu docelowego konwersji. W związku z tym instrukcjeS 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 typuS
środowiska uruchomieniowego przy użyciuis
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 typobject
. Na przykład następujące elementystruct 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ścip
. ZamiastPoint
tegoclass
zadeklarowano wartość 20, ponieważp
ibox
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
ii
oba stosują niejawne konwersje dynamiczne, w których powiązanie operacji jest zawieszone do czasu wykonywania. W czasie wykonywania wyszukiwane są niejawne konwersje typu czasud
wykonywania (string
) do typu docelowego. Znaleziono konwersję,string
ale nie doint
.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 typsbyte
, ,byte
,short
uint
ushort
, lubulong
, pod warunkiem, że wartość constant_expression mieści się w zakresie typu docelowego. - Constant_expression typu
long
można przekonwertować na typulong
, 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 klasyC
bazowej , zT
do dowolnej klasy bazowejC
, i zT
do dowolnego interfejsu zaimplementowanego przezC
. - Od
T
do interface_typeI
wT
efektywnym zestawie interfejsów i zT
do dowolnego podstawowego interfejsuI
. - Od
T
do parametruU
typu, któryT
zależyU
od (§15.2.5).Uwaga: ponieważ
T
jest znany jako typ odwołania, w zakresieT
, typU
czasu wykonywania zawsze będzie typem odwołania, nawet jeśliU
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 klasyC
bazowej , zT
do dowolnej klasy bazowejC
, i zT
do dowolnego interfejsu zaimplementowanego przezC
.Uwaga:
C
będzie jednym z typówSystem.Object
,System.ValueType
lubSystem.Enum
(w przeciwnym razieT
jest znany jako typ odwołania). notatka końcowa - Od
T
do interface_typeI
wT
efektywnym zestawie interfejsów i zT
do dowolnego podstawowego interfejsuI
.
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 T
typu istnieją następujące dalsze niejawne konwersje:
- Od do typu
S
odwołania, jeśli ma niejawną konwersję na typS₀
odwołania iS₀
ma konwersję tożsamości naS
.T
W czasie wykonywania konwersja jest wykonywana w taki sam sposób, jak konwersja naS₀
. - Od
T
do typuI
interfejsu, jeśli ma niejawną konwersję na typI₀
interfejsu , iI₀
jest wariancja-cabrio doI
(§18.2.3.3). W czasie wykonywania, jeśliT
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 T
System.ValueTuple<...>
mu typu i zainicjowanie każdego z jego pól w kolejności od lewej do prawej przez obliczenie odpowiedniego wyrażenia E
elementu 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
,t2
t4
it5
są prawidłowe, ponieważ niejawne konwersje istnieją z wyrażeń elementów do odpowiednich typów elementów. Deklaracja elementu jest nieprawidłowat3
, ponieważ nie ma konwersji znull
naint
. Deklaracjat5
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
dobyte
, ,uint
ushort
, ,ulong
lubchar
. - Od
byte
do lubchar
sbyte
. - Z
short
dosbyte
, ,byte
,uint
ushort
, ,ulong
lubchar
. - Z
ushort
dosbyte
,byte
,short
lubchar
. - Z
int
dosbyte
, ,short
uint
ulong
byte
ushort
lub .char
- Z
uint
dosbyte
, ,byte
,ushort
short
, ,int
lubchar
. - Z
long
dosbyte
, ,short
int
ushort
uint
byte
,ulong
lub .char
- Z
ulong
dosbyte
, ,short
int
ushort
uint
byte
,long
lub .char
- Z
char
dosbyte
,byte
lubshort
. - Z
float
dosbyte
,byte
,short
long
ushort
uint
ulong
int
, ,char
lub .decimal
- Z
double
dosbyte
, ,short
int
uint
ushort
byte
ulong
char
long
float
lub .decimal
- Z
decimal
dosbyte
, ,short
int
uint
ushort
byte
ulong
char
long
float
lub .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łaszaSystem.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
lubdouble
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
.
- Jeśli wartość operandu to NaN lub nieskończona,
- 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 zaznaczonym kontekście konwersja jest kontynuowana w następujący sposób:
- W przypadku konwersji z
double
nafloat
wartośćdouble
wartość jest zaokrąglona do najbliższejfloat
wartości.double
Jeśli wartość jest za mała, aby reprezentować jakofloat
wartość , wynik stanie się zerowy z tym samym znakiem co wartość. Jeśli wielkośćdouble
wartości jest zbyt duża, aby reprezentować jakofloat
wartość , 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).decimal
double
float
- Jeśli wartość źródłowa jest zbyt mała, aby reprezentować jako
decimal
wartość , wynik staje się zerowy, zachowując znak oryginalnej wartości, jeślidecimal
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.
- Jeśli wartość źródłowa jest zbyt mała, aby reprezentować jako
- W przypadku konwersji z
decimal
do lubfloat
decimal
double
wartość jest zaokrąglona do najbliższejdouble
lubfloat
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ż zakresfloat
wartości idouble
, ale nie jest gwarantowany. W przypadkudecimal
reprezentacji bez wartości niedociągnięć lub wartości NaN i z zakresem mniejszym niżfloat
wynik konwersji zdecimal
nafloat
albodouble
nigdy nie będzie nieskończoność lub NaN. notatka końcowa
10.3.3 Jawne konwersje wyliczenia
Jawne konwersje wyliczenia to:
- Z
sbyte
, ,short
ushort
int
long
char
ulong
uint
double
byte
float
lubdecimal
do dowolnej enum_type. - Z dowolnego enum_type do
sbyte
,byte
,int
uint
long
ushort
short
char
float
ulong
double
lub .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 typemint
, konwersja zE
nabyte
jest przetwarzana jako jawna konwersja liczbowa (§10.3.2) z dobyte
, a konwersja zbyte
int
naE
jest przetwarzana jako niejawna konwersja liczbowa (§10.2.3) zbyte
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
, podanaS
jest klasą bazową .T
- Z dowolnego class_type do żadnego interface_type
T
S
, dostarczoneS
nie jest zapieczętowane i dostarczoneS
nie implementuje .T
- Z dowolnego interface_type do dowolnego class_type
S
T
, podanyT
nie jest zapieczętowany ani dostarczanyT
implementuje .S
- Z dowolnego interface_type do dowolnego interface_type
S
T
, podanyS
nie pochodzi z .T
- Z array_type
S
z typemSᵢ
elementu do array_typeT
z typemTᵢ
elementu , pod warunkiem, że wszystkie następujące elementy są spełnione:S
iT
różnią się tylko w typie elementu. Innymi słowy,S
iT
mają taką samą liczbę wymiarów.- Jawna konwersja odwołania istnieje z
Sᵢ
doTᵢ
.
- 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 zS
doT
.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 typuT[]
tablicy , pod warunkiem, że istnieje konwersja tożsamości lub jawna konwersja odwołania zS
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 zS
do typuT₀
odwołania iT₀
istnieje konwersja tożsamości zT₀
naT
. - Od typu
S
odwołania do interfejsu lub typuT
delegata, jeśli istnieje jawna konwersja odwołania zS
do interfejsu lub typuT₀
delegata iT₀
albo jest wariancja-cabrio doT
lubT
jest wariancja-cabrio doT₀
§18.2.3.3. - Od
D<S₁...Sᵥ>
lokalizacji, gdzieD<T₁...Tᵥ>
D<X₁...Xᵥ>
jest ogólnym typem delegata,D<S₁...Sᵥ>
nie jest zgodny z lub identycznyD<T₁...Tᵥ>
z parametrem , i dla każdego parametruXᵢ
typu następującegoD
blokady:- Jeśli
Xᵢ
zmienna jest niezmienna,Sᵢ
wartość jest taka sama jakTᵢ
. - Jeśli
Xᵢ
jest kowariantny, istnieje konwersja tożsamości, niejawna konwersja odwołania lub jawna konwersja odwołania zSᵢ
doTᵢ
. - Jeśli
Xᵢ
jest kontrawariantny, toSᵢ
iTᵢ
są identyczne lub oba typy odwołań.
- Jeśli
- 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 T
System.ValueTuple<...>
mu typu i zainicjowanie każdego z jego pól w kolejności od lewej do prawej przez obliczenie odpowiedniego wyrażenia E
elementu 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_type
I
, w którym istnieje konwersja rozpboxowania z interface_type do typu non_nullable_valueI₀
i konwersja tożsamości zI
na .I₀
- Od dowolnego interface_type do dowolnego non_nullable_value_type
I
, w którym istnieje konwersja rozpakowa z interface_typeI₀
do non_nullable_value_type iI₀
albo jest variance_convertible doI
lubI
jest wariancja-kabriolet doI₀
(§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 instrukcjeobject 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 doT
i z dowolnej klasy bazowejC
T
doT
klasy . - Z dowolnego interface_type do
T
. - Od
T
do dowolnego interface_typeI
pod warunkiem, że nie ma jeszcze niejawnej konwersji odwołania zT
doI
. - Od type_parameter
U
doT
podanego, żeT
zależy odU
(§15.2.5).Uwaga: ponieważ
T
jest znany jako typ odwołania, w zakresieT
, typ czasu wykonywania zawsze będzie typem odwołania, nawet jeśliU
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 doT
i z dowolnej klasy bazowejC
T
doT
klasy .Uwaga: C będzie jednym z typów
System.Object
,System.ValueType
lubSystem.Enum
(w przeciwnym razieT
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_typeI
pod warunkiem, że nie ma jeszcze niejawnej konwersji zT
doI
. Ta konwersja składa się z niejawnej konwersji boksu (§10.2.9) zT
doobject
, po której następuje jawna konwersja odwołania zobject
doI
. W czasie wykonywania, jeśliT
jest typem wartości, konwersja jest wykonywana jako konwersja boksu, po której następuje jawna konwersja odwołania. W czasie wykonywania, jeśliT
jest typem odwołania, konwersja jest wykonywana jako jawna konwersja odwołania. - Od parametru
U
typu doT
podanego, któryT
zależyU
od (§15.2.5). W czasie wykonywania, jeśliT
jest typem wartości iU
jest typem referencyjnym, konwersja jest wykonywana jako konwersja rozpieczętowana. W czasie wykonywania, jeśli zarównoT
typy wartości, jak iU
są, iT
muszą być tego samego typu iU
nie jest wykonywana żadna konwersja. W czasie wykonywania, jeśliT
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ć, żeX<int>.F(7)
zwróci7L
wartość . 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ć polaint
bezpośrednio nalong
element .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 typA
B
i od typuB
do typuA
. 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
T
docelowego , 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₀
iT₀
są różnymi typami.- Albo
S₀
jest klasą lubT₀
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
doT
lub zT
doS
.
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 nieA
B
są interface_types
, mówi się, że jest uwzględniana przezB
, iB
mówi się,A
że obejmuje .A
- Jeśli standardowa niejawna konwersja (§10.4.2) istnieje z wyrażenia
E
na typB
, a jeśli ani typB
E
(jeśli go ma) nie są interface_types
,E
mówi się, że zostanie objęteB
, iB
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₀
iT₀
.- Jeśli
E
ma typ, niechS
będzie to typ. - Jeśli
S
lubT
są typami wartości dopuszczania wartości null, niechSᵢ
iTᵢ
będą ich podstawowymi typami, w przeciwnym razie letSᵢ
iTᵢ
beS
iT
, odpowiednio. - Jeśli
Sᵢ
lubTᵢ
są parametrami typu, let i be ich efektywne klasy bazowe, w przeciwnym razie letS₀
S₀
iT₀
beSₓ
iT₀
Tᵢ
, odpowiednio.
- Jeśli
Znajdź zestaw typów ,
D
z którego zostaną uwzględnione operatory konwersji zdefiniowane przez użytkownika. Ten zestaw składa się zS₀
(jeśliS₀
istnieje i jest klasą lub strukturą), klas bazowychS₀
(jeśliS₀
istnieje i jest klasą) orazT₀
(jeśliT₀
jest klasą lub strukturą). Typ jest dodawany do zestawuD
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 wD
tej konwersji z typu obejmującego typ obejmujący typ obejmującyE
przezT
element . JeśliU
jest pusty, konwersja jest niezdefiniowana i występuje błąd czasu kompilacji.- Jeśli
S
istnieje i którykolwiek z operatorów wU
konwersji zS
, toSₓ
jestS
. Sₓ
W przeciwnym razie jest najbardziej obejmującym typem w połączonym zestawie typów źródłowych operatorów w programieU
. Jeśli nie można odnaleźć dokładnie jednego najbardziej objętego typu, konwersja jest niejednoznaczna i wystąpi błąd czasu kompilacji.
- Jeśli
Znajdź najbardziej specyficzny typ docelowy,
Tₓ
, operatorów w plikuU
:- Jeśli którykolwiek z operatorów w
U
konwersji naT
, toTₓ
ma wartośćT
. Tₓ
W przeciwnym razie jest najbardziej obejmującym typem w połączonym zestawie typów docelowych operatorów w systemieU
. Jeśli nie można odnaleźć dokładnie jednego typu obejmującego, konwersja jest niejednoznaczna i występuje błąd czasu kompilacji.
- Jeśli którykolwiek z operatorów w
Znajdź najbardziej specyficzny operator konwersji:
- Jeśli
U
zawiera dokładnie jeden operator konwersji zdefiniowany przez użytkownika, który konwertujeSₓ
z naTₓ
, jest to najbardziej specyficzny operator konwersji. - W przeciwnym razie, jeśli
U
zawiera dokładnie jeden operator konwersji zniesionej, który konwertuje zSₓ
naTₓ
, jest to najbardziej specyficzny operator konwersji. - W przeciwnym razie konwersja jest niejednoznaczna i występuje błąd czasu kompilacji.
- Jeśli
Na koniec zastosuj konwersję:
- Jeśli E nie ma jeszcze typu
Sₓ
, wykonywana jest standardowa niejawna konwersja zE
doSₓ
. - Najbardziej specyficzny operator konwersji jest wywoływany w celu konwersji z
Sₓ
naTₓ
. - Jeśli
Tₓ
nieT
ma wartości , wykonywana jest standardowa niejawna konwersja zTₓ
doT
.
- Jeśli E nie ma jeszcze typu
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₀
iT₀
.- Jeśli
E
ma typ, niechS
będzie to typ. - Jeśli
S
lubT
są typami wartości dopuszczania wartości null, niechSᵢ
iTᵢ
będą ich podstawowymi typami, w przeciwnym razie letSᵢ
iTᵢ
beS
iT
, odpowiednio. - Jeśli
Sᵢ
lubTᵢ
są parametrami typu, let i be ich efektywne klasy bazowe, w przeciwnym razie letS₀
S₀
iT₀
beSᵢ
iT₀
Tᵢ
, odpowiednio.
- Jeśli
- Znajdź zestaw typów ,
D
z którego zostaną uwzględnione operatory konwersji zdefiniowane przez użytkownika. Ten zestaw składa się zS₀
(jeśliS₀
istnieje i jest klasą lub strukturą), klas bazowychS₀
(jeśliS₀
istnieje i jest klasą),T₀
(jeśliT₀
jest klasą lub strukturą) oraz klasamiT₀
bazowymi (jeśliT₀
jest klasą).A
Typ jest dodawany do zestawuD
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 wD
tej konwersji z typu obejmującego lubS
objętegoE
(jeśli istnieje) do typu obejmującego lub objętego przezT
program . JeśliU
jest pusty, konwersja jest niezdefiniowana i występuje błąd czasu kompilacji. - Znajdź najbardziej specyficzny typ źródła,
Sₓ
, operatorów w plikuU
:- Jeśli S istnieje i którykolwiek z operatorów w
U
konwersji zS
, toSₓ
jestS
. - 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 systemieU
. Jeśli nie można odnaleźć dokładnie jednego typu obejmującego, konwersja jest niejednoznaczna i występuje błąd czasu kompilacji.
- Jeśli S istnieje i którykolwiek z operatorów w
- Znajdź najbardziej specyficzny typ docelowy,
Tₓ
, operatorów w plikuU
:- Jeśli którykolwiek z operatorów w
U
konwersji naT
, toTₓ
ma wartośćT
. - W przeciwnym razie, jeśli którykolwiek z operatorów w
U
konwersji na typy, które są objęte przezT
,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 systemieU
. Jeśli nie można odnaleźć najbardziej objętego typu, konwersja jest niejednoznaczna i wystąpi błąd czasu kompilacji.
- Jeśli którykolwiek z operatorów w
- Znajdź najbardziej specyficzny operator konwersji:
- Jeśli U zawiera dokładnie jednego operatora konwersji zdefiniowanego przez użytkownika, który konwertuje
Sₓ
z naTₓ
, jest to najbardziej specyficzny operator konwersji. - W przeciwnym razie, jeśli
U
zawiera dokładnie jeden operator konwersji zniesionej, który konwertuje zSₓ
naTₓ
, jest to najbardziej specyficzny operator konwersji. - W przeciwnym razie konwersja jest niejednoznaczna i występuje błąd czasu kompilacji.
- Jeśli U zawiera dokładnie jednego operatora konwersji zdefiniowanego przez użytkownika, który konwertuje
- Na koniec zastosuj konwersję:
- Jeśli
E
nie ma jeszcze typuSₓ
, wykonywana jest standardowa jawna konwersja z E naSₓ
. - Najbardziej specyficzny operator konwersji zdefiniowany przez użytkownika jest wywoływany w celu konwersji z
Sₓ
naTₓ
. - Jeśli
Tₓ
nieT
ma wartości , wykonywana jest standardowa jawna konwersja zTₓ
doT
.
- Jeśli
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?
doT?
- Niejawna lub jawna konwersja z
S
doT?
- Jawna konwersja z
S?
naT
.
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?
doT?
:- Jeśli wartość źródłowa ma wartość null (
HasValue
właściwość tofalse
), wynikiem jest wartość null typuT?
. - W przeciwnym razie konwersja jest obliczana jako rozpasanie z
S?
doS
, a następnie konwersja bazowa zS
naT
, a następnie zawijanie zT
doT?
.
- Jeśli wartość źródłowa ma wartość null (
- Jeśli konwersja dopuszczana do wartości null pochodzi z
S
do , konwersja jest obliczana jako konwersja bazowa zS
do, po której następuje zawijanie zT
doT
T?
.T?
- Jeśli konwersja dopuszczana do wartości null wynosi od
S?
do , konwersja jest obliczana jako rozpasanie zS?
do, po której następuje konwersja bazowa zS
naS
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 T
wartoś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, toD
iF
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 parametremD
wyjściowym. - Jeśli
F
ma jawnie wpisaną listę parametrów, każdy parametr w programieD
ma te same modyfikatory co odpowiedni parametr wF
programie, a konwersja tożsamości istnieje między odpowiednim parametrem wF
pliku . - 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 iD
ma«TaskType»
typ zwracany (§15.15.1), kiedy każdy parametrF
ma typ odpowiedniego parametru wD
, 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 i
D
ma typ zwracany void lubF
jest asynchroniczny iD
ma typ zwracany«TaskType»
, wówczas, gdy każdy parametr ma typ odpowiedniego parametruF
wD
, treśćF
F
jest prawidłowym blokiem (w.r.t §13.3), w którym żadna instrukcja niereturn
określa wyrażenia. - Jeśli treść
F
jest wyrażeniem, a alboF
nie jest asynchroniczne iD
mavoid
typ niewzrótnyT
, lubF
jest asynchroniczny iD
ma«TaskType»<T>
typ zwracany (§15.15.1), to gdy każdy parametrF
ma typ odpowiadającego parametru wD
, treśćF
jest prawidłowym wyrażeniem (w.r.t §12), które jest niejawnie konwertowane naT
. - Jeśli treść obiektu jest blokiem i
F
albo nie jest asynchroniczny iD
ma niepusty typT
zwracany , lubF
jest asynchroniczny iD
ma typ zwracany, gdy każdy parametrF
jest podany typ odpowiedniego parametru wD
, 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 typuA
i zwraca wartość typuR
: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 przypadkux
danego typuint
x + 1
jest prawidłowe wyrażenie niejawnie konwertowane na typint
.Podobnie drugie przypisanie pomyślnie konwertuje funkcję anonimową na typ delegata Func<int,dwukrotnie> , ponieważ wynik
x + 1
(typuint
) jest niejawnie konwertowany na typdouble
.Jednak trzecie przypisanie jest błędem czasu kompilacji, ponieważ w przypadku
x
danego typudouble
wynikx + 1
(typudouble
) nie jest niejawnie konwertowany na typint
.Czwarte przypisanie pomyślnie konwertuje anonimową funkcję asynchronicznie na typ
Func<int, Task<int>>
delegata, ponieważ wynikx + 1
(typuint
) jest niejawnie konwertowany na efektywny typint
zwracany asynchronicznego lambda, który ma typTask<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 D
delegata . 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) formularzaE(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
,out
lubref
) odpowiedniego parametru w parameter_listD
— z wyjątkiem parametrów typudynamic
, gdzie odpowiadające wyrażenie ma typobject
zamiastdynamic
. - 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
.
- Lista
- Konwersja jest uważana za istniejącą, jeśli algorytm §12.8.10.2 tworzy jedną najlepszą metodę
M
zgodną (§20.4) zD
. - Jeśli wybrana metoda
M
jest metodą wystąpienia, wyrażenie wystąpienia skojarzone zE
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ść typuD1
.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 E
lub 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ń.
ECMA C# draft specification