Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
10.1 Generale
Una conversione fa sì che un'espressione venga convertita o considerata come di un tipo specifico. Nel caso precedente una conversione può comportare una modifica nella rappresentazione. Le conversioni possono essere implicite o esplicite e determina se è necessario un cast esplicito.
Esempio: ad esempio, la conversione dal tipo
intal tipolongè implicita, pertanto le espressioni di tipointpossono essere considerate in modo implicito come tipolong. La conversione opposta, dal tipo al tipolongint, è esplicita e pertanto è necessario un cast esplicito.int a = 123; long b = a; // implicit conversion from int to long int c = (int) b; // explicit conversion from long to intesempio finale
Alcune conversioni sono definite dal linguaggio. I programmi possono anche definire le proprie conversioni (§10.5).
Alcune conversioni nel linguaggio vengono definite dalle espressioni ai tipi, altre dai tipi ai tipi. Una conversione da un tipo si applica a tutte le espressioni con tale tipo.
Esempio:
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;esempio finale
10.2 Conversioni implicite
10.2.1 Generale
Le conversioni seguenti vengono classificate come conversioni implicite:
- Conversioni di identità (§10.2.2)
- Conversioni numeriche implicite (§10.2.3)
- Conversioni di enumerazione implicita (§10.2.4)
- Conversioni implicite di stringhe interpolate (§10.2.5)
- Conversioni di riferimenti implicite (§10.2.8)
- Conversioni boxing (§10.2.9)
- Conversioni dinamiche implicite (§10.2.10)
- Conversioni implicite dei parametri di tipo (§10.2.12)
- Conversioni implicite di espressioni costanti (§10.2.11)
- Conversioni implicite definite dall'utente (incluse quelle lifted) (§10.2.14)
- Conversioni di funzioni anonime (§10.2.15)
- Conversioni dei gruppi di metodi (§10.2.15)
- Conversioni di valori letterali Null (§10.2.7)
- Conversioni nullable implicite (§10.2.6)
- Conversioni di tuple implicite (§10.2.13)
- Conversioni letterali predefinite (§10.2.16)
- Conversioni di throw implicite (§10.2.17)
Le conversioni implicite possono verificarsi in diverse situazioni, incluse le chiamate ai membri della funzione (§12.6.6), le espressioni cast (§12.9.8) e le assegnazioni (§12.23).
Le conversioni implicite predefinite hanno sempre esito positivo e non generano mai eccezioni.
Nota: anche le conversioni implicite definite dall'utente progettate correttamente devono presentare queste caratteristiche. nota finale
Ai fini della conversione, i tipi object e dynamic sono identity convertibile (§10.2.2).
Tuttavia, le conversioni dinamiche (§10.2.10) si applicano solo alle espressioni di tipo dynamic (§8.2.4).
10.2.2 Conversione di identità
Una conversione di identità converte da qualsiasi tipo allo stesso tipo o a un tipo equivalente in fase di esecuzione. Un motivo per cui questa conversione esiste è in modo che un tipo T o un'espressione di tipo T possa essere convertito in T se stesso. Esistono le conversioni di identità seguenti:
- Tra
TeT, per qualsiasi tipoT. - Tra
TeT?per qualsiasi tipo diTriferimento . - Tra
objectedynamic. - Tra tutti i tipi di tupla con la stessa arità e il tipo costruito
ValueTuple<...>corrispondente, quando esiste una conversione di identità tra ogni coppia di tipi di elemento corrispondenti. - Tra i tipi costruiti dallo stesso tipo generico in cui esiste una conversione identity tra ogni argomento di tipo corrispondente.
Esempio: di seguito viene illustrata la natura ricorsiva della terza regola:
(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;I tipi di tuple e tutti hanno due elementi: un
t1seguito da un oggettot2.t3intstringI tipi di elemento tupla possono essere usati da tuple, come int4,t5et6. Esiste una conversione di identità tra ogni coppia di tipi di elemento corrispondenti, incluse le tuple annidate, pertanto esiste una conversione di identità tra i tipi di tuplet4,t5et6.esempio finale
Tutte le conversioni di identità sono simmetriche. Se esiste una conversione di identità da T₁ a T₂, esiste una conversione di identità da T₂ a T₁. Due tipi sono convertibili in identità quando esiste una conversione di identità tra due tipi.
Nella maggior parte dei casi, una conversione di identità non ha alcun effetto in fase di esecuzione. Tuttavia, poiché le operazioni a virgola mobile possono essere eseguite con precisione superiore rispetto a quanto previsto dal tipo (§8.3.7), l'assegnazione dei risultati può comportare una perdita di precisione e cast espliciti sono garantiti per ridurre la precisione a quanto previsto dal tipo (§12.9.8).
10.2.3 Conversioni numeriche implicite
Le conversioni numeriche implicite sono:
- Da
sbyteashort,int,longfloat,double, odecimal. - Da
byteashort,ushortint,uint,long,ulong,float, ,doubleodecimal. - Da
shortaint,longfloat, ,doubleodecimal. - Da
ushortaint,uint,longulong,float, ,doubleodecimal. - Da
intalong,floatdouble, odecimal. - Da
uintalong,ulongfloat, ,doubleodecimal. - Da
longafloat,doubleodecimal. - Da
ulongafloat,doubleodecimal. - Da
charaushort,int,uintlong,ulong,float, ,doubleodecimal. - Da
floatadouble.
Le conversioni da int, uinto longulong a float e da long o ulong a double possono causare una perdita di precisione, ma non causeranno mai una perdita di grandezza. Le altre conversioni numeriche implicite non perdono mai informazioni.
Non esistono conversioni implicite predefinite nel char tipo, pertanto i valori degli altri tipi integrali non vengono convertiti automaticamente nel char tipo.
10.2.4 Conversioni di enumerazione implicita
Una conversione di enumerazione implicita consente a un constant_expression (§12.25) con qualsiasi tipo integer e il valore zero da convertire in qualsiasi enum_type e in qualsiasi nullable_value_type il cui tipo sottostante è un enum_type. In quest'ultimo caso la conversione viene valutata convertendo nel enum_type sottostante e eseguendo il wrapping del risultato (§8.3.12).
10.2.5 Conversioni implicite di stringhe interpolate
Una conversione implicita di stringhe interpolate consente la conversione di un interpolated_string_expression (§12.8.3
Quando viene applicata questa conversione, un valore stringa non è composto dalla stringa interpolata. Viene invece creata un'istanza di System.FormattableString , come descritto più avanti in §12.8.3.
10.2.6 Conversioni implicite nullable
Le conversioni nullable implicite sono quelle conversioni nullable (§10.6.1) derivate da conversioni implicite predefinite.
Conversioni letterali Null 10.2.7
Esiste una conversione implicita dal null valore letterale a qualsiasi tipo riferimento o tipo di valore nullable. Questa conversione produce un riferimento Null se il tipo di destinazione è un tipo riferimento o il valore Null (§8.3.12) del tipo di valore nullable specificato.
10.2.8 Conversioni di riferimento implicite
Le conversioni di riferimento implicite sono:
- Da qualsiasi reference_type a
objectedynamic. - Da qualsiasi class_type
Sa qualsiasi class_typeT, fornitoSè derivato daT. - Da qualsiasi , fornito
Simplementa . - Da qualsiasi interface_type
Sa qualsiasi interface_typeT, fornitoSè derivato daT. - Da un array_type
Scon un tipo diSᵢelemento a un array_typeTcon un tipo diTᵢelemento , purché tutte le condizioni seguenti siano vere:-
SeTdifferiscono solo in tipo di elemento. In altre parole,SeThanno lo stesso numero di dimensioni. - Esiste una conversione di riferimento implicita da
SᵢaTᵢ.
-
- Da un tipo di
S[]matrice unidimensionale aSystem.Collections.Generic.IList<T>,System.Collections.Generic.IReadOnlyList<T>e dalle relative interfacce di base, purché sia presente una conversione implicita di identità o riferimento daSaT. - Da qualsiasi array_type a
System.Arraye dalle interfacce implementate. - Da qualsiasi delegate_type a
System.Delegatee le interfacce implementate. - Dal valore letterale null (§6.4.5.7) a qualsiasi tipo di riferimento.
- Da qualsiasi
- Da qualsiasi reference_type a un'interfaccia o a un tipo
Tdelegato se ha un'identità implicita o una conversione di riferimento in un tipoT₀di interfaccia o delegato edT₀è convertibile a varianza (§19.2.3.3) inT. - Conversioni implicite che coinvolgono parametri di tipo noti come tipi di riferimento. Per altre informazioni sulle conversioni implicite che coinvolgono parametri di tipo, vedere §10.2.12 .
Le conversioni di riferimento implicite sono quelle conversioni tra reference_typeche possono essere dimostrate sempre riuscite e pertanto non richiedono controlli in fase di esecuzione.
Le conversioni di riferimento, implicite o esplicite, non modificano mai l'identità referenziale dell'oggetto da convertire.
Nota: mentre una conversione di riferimento può modificare il tipo del riferimento, non modifica mai il tipo o il valore dell'oggetto a cui viene fatto riferimento. nota finale
Conversioni boxing 10.2.9
Una conversione boxing consente a un value_type di essere convertito in modo implicito in un reference_type. Esistono le conversioni boxing seguenti:
- Da qualsiasi value_type al tipo
object. - Da qualsiasi value_type al tipo
System.ValueType. - Da qualsiasi enum_type al tipo
System.Enum. - Da qualsiasi non_nullable_value_type a qualsiasi interface_type implementata dal non_nullable_value_type.
- Da qualsiasi non_nullable_value_type a qualsiasi interface_type
Iin modo che sia presente una conversione boxing dal non_nullable_value_type a un altro interface_typeI₀eI₀abbia una conversione di identità in .I - Da qualsiasi non_nullable_value_type a qualsiasi interface_type
Iin modo che sia presente una conversione boxing dal non_nullable_value_type a un altro interface_typeI₀edI₀è convertibile a varianza (§19.2.3.3) aI. - Da qualsiasi nullable_value_type a qualsiasi reference_type in cui è presente una conversione boxing dal tipo sottostante del nullable_value_type al reference_type.
- Da un parametro di tipo che non è noto per essere un tipo riferimento a qualsiasi tipo in modo che la conversione sia consentita da §10.2.12.
La conversione boxing di un valore non nullable-value-type consiste nell'allocare un'istanza dell'oggetto e copiare il valore in tale istanza.
La conversione boxing di un valore di un nullable_value_type produce un riferimento Null se è il valore Null (HasValue è false) oppure il risultato di unwrapping e conversione boxing del valore sottostante in caso contrario.
Nota: il processo di boxing può essere immaginato in termini di esistenza di una classe boxing per ogni tipo di valore. Si consideri, ad esempio, un'implementazione di un'interfaccia
struct SI, con una classe boxing denominataS_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(); } }La conversione boxing di un valore
vdi tipoSconsiste ora nell'esecuzione dell'espressionenew S_Boxing(v)e nella restituzione dell'istanza risultante come valore del tipo di destinazione della conversione. Di conseguenza, le istruzioniS s = new S(); object box = s;può essere considerato simile a:
S s = new S(); object box = new S_Boxing(s);Il tipo di boxing immaginato descritto sopra non esiste effettivamente. Al contrario, un valore boxed di tipo
Sha il tipoSdi runtime e un controllo del tipo di runtime usando l'operatoreiscon un tipo valore come operando destro verifica se l'operando sinistro è una versione boxed dell'operando destro. ad esempio:int i = 123; object box = i; if (box is int) { Console.Write("Box contains an int"); }restituirà quanto segue:
Box contains an intUna conversione boxing implica la creazione di una copia del valore sottoposto a boxing. Questo comportamento è diverso da una conversione di un derivato . Ad esempio, il codice seguente
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); } }restituisce il valore 10 nella console perché l'operazione di boxing implicita che si verifica nell'assegnazione di
pper fareboxin modo che il valore dipvenga copiato. EraPointstato dichiarato invece un oggettoclass, il valore 20 verrebbe restituito perchépeboxfarà riferimento alla stessa istanza.L'analogia di una classe boxing non deve essere usata come strumento più che uno strumento utile per illustrare il funzionamento concettuale del boxing. Esistono numerose piccole differenze tra il comportamento descritto da questa specifica e il comportamento che potrebbe derivare dall'implementazione boxing in questo modo.
nota finale
10.2.10 Conversioni dinamiche implicite
Esiste una conversione dinamica implicita da un'espressione di tipo dinamico a qualsiasi tipo T. La conversione è associata dinamicamente a §12.3.3, il che significa che una conversione implicita verrà ricercata in fase di esecuzione dal tipo di runtime dell'espressione a T. Se non viene trovata alcuna conversione, viene generata un'eccezione di runtime.
Questa conversione implicita viola apparentemente il consiglio all'inizio di §10.2 che una conversione implicita non dovrebbe mai causare un'eccezione. Tuttavia, non è la conversione stessa, ma la ricerca della conversione che causa l'eccezione. Il rischio di eccezioni in fase di esecuzione è intrinseco nell'uso dell'associazione dinamica. Se l'associazione dinamica della conversione non è desiderata, l'espressione può essere prima convertita in objecte quindi nel tipo desiderato.
Esempio: di seguito vengono illustrate le conversioni dinamiche implicite:
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 existsLe assegnazioni a
s2eientrambe usano conversioni dinamiche implicite, in cui l'associazione delle operazioni viene sospesa fino al runtime. In fase di esecuzione, vengono cercate conversioni implicite dal tipo di runtime did(string) al tipo di destinazione. Viene trovata una conversione instringma non inint.esempio finale
10.2.11 Conversioni implicite di espressioni costanti
Una conversione implicita di espressioni costanti consente le conversioni seguenti:
- Un constant_expression di tipo (
int) può essere convertito in tiposbyte,byteshortushortuintoulong, purché il valore dell'constant_expression sia compreso nell'intervallo del tipo di destinazione. - Un constant_expression di tipo
longpuò essere convertito in tipoulong, a condizione che il valore del constant_expression non sia negativo.
10.2.12 Conversioni implicite che coinvolgono parametri di tipo
Per un noto come tipo riferimento (T), esistono le conversioni di riferimento implicite seguenti (§10.2.8):
- Da
Talla classeCbase effettiva , daTa qualsiasi classe di base diCe daTa qualsiasi interfaccia implementata daC. - Da
Ta un interface_typeInelTset di interfacce effettivo e daTa qualsiasi interfaccia di base diI. - Da
Ta un parametroUdi tipo specificato cheTdipende daU(§15.2.5).Nota: poiché
Tè noto come tipo riferimento, nell'ambito diT, il tipo di runtime diUsarà sempre un tipo riferimento, anche seUnon è noto come tipo riferimento in fase di compilazione. nota finale - Dal valore letterale null (§6.4.5.7) a T.
Per un type_parameterT che non è noto come tipo riferimento §15.2.5, le conversioni seguenti che coinvolgono T sono considerate conversioni boxing (§10.2.9) in fase di compilazione. In fase di esecuzione, se T è un tipo di valore, la conversione viene eseguita come conversione boxing. In fase di esecuzione, se T è un tipo riferimento, la conversione viene eseguita come conversione implicita dei riferimenti o conversione di identità.
- Da
Talla classeCbase effettiva , daTa qualsiasi classe di base diCe daTa qualsiasi interfaccia implementata daC.Nota:
Csarà uno dei tipiSystem.Object,System.ValueTypeoSystem.Enum(in caso contrarioTsarebbe noto come tipo riferimento). nota finale - Da
Ta un interface_typeInelTset di interfacce effettivo e daTa qualsiasi interfaccia di base diI.
Per un che nonTnoto come tipo riferimento, è presente una conversione implicita da a un parametro T di tipo fornito U dipende da T. In fase di esecuzione, se T è un tipo valore e U è un tipo riferimento, la conversione viene eseguita come conversione boxing. In fase di esecuzione, se e TU sono tipi valore, T e U sono necessariamente lo stesso tipo e non viene eseguita alcuna conversione. In fase di esecuzione, se T è un tipo riferimento, U è necessariamente anche un tipo riferimento e la conversione viene eseguita come conversione implicita di riferimenti o conversione di identità (§15.2.5).
Per un determinato parametro Tdi tipo esistono altre conversioni implicite seguenti:
- Da
Ta un tipo riferimentoSse ha una conversione implicita in un tipo riferimentoS₀eS₀ha una conversione identity inS. In fase di esecuzione, la conversione viene eseguita allo stesso modo della conversione inS₀. - Da
Ta un tipo diIinterfaccia se ha una conversione implicita in un tipo diI₀interfaccia edI₀è convertibile in varianza inI(§19.2.3.3). In fase di esecuzione, seTè un tipo di valore, la conversione viene eseguita come conversione boxing. In caso contrario, la conversione viene eseguita come conversione implicita di riferimento o di identità.
In tutti i casi, le regole assicurano che una conversione venga eseguita come conversione boxing se e solo se in fase di esecuzione la conversione proviene da un tipo valore a un tipo riferimento.
10.2.13 Conversioni di tuple implicite
Esiste una conversione implicita da un'espressione E di tupla a un tipo T di tupla se E ha la stessa arità di T e esiste una conversione implicita da ogni elemento in E al tipo di elemento corrispondente in T. La conversione viene eseguita creando un'istanza del Ttipo corrispondente System.ValueTuple<...> e inizializzando ognuno dei relativi campi in ordine da sinistra a destra valutando l'espressione dell'elemento tupla corrispondente di E, convertendola nel tipo di elemento corrispondente di T utilizzando la conversione implicita trovata e inizializzando il campo con il risultato.
Se un nome di elemento nell'espressione di tupla non corrisponde a un nome di elemento corrispondente nel tipo di tupla, verrà generato un avviso.
Esempio:
(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 ignoredLe dichiarazioni di ,
t1t2et4sono tutte valide, poiché esistono conversioni implicite dalle espressioni dit5elemento ai tipi di elemento corrispondenti. La dichiarazione dit3non è valida perché non è presente alcuna conversione danullaint. La dichiarazione dit5genera un avviso perché i nomi degli elementi nell'espressione di tupla differiscono da quelli nel tipo di tupla.esempio finale
10.2.14 Conversioni implicite definite dall'utente
Una conversione implicita definita dall'utente è costituita da una conversione implicita standard facoltativa, seguita dall'esecuzione di un operatore di conversione implicita definito dall'utente, seguita da un'altra conversione implicita standard facoltativa. Le regole esatte per la valutazione delle conversioni implicite definite dall'utente sono descritte in §10.5.4.
10.2.15 Conversioni di funzioni anonime e conversioni di gruppi di metodi
Le funzioni anonime e i gruppi di metodi non dispongono di tipi in e di se stessi, ma possono essere convertiti in modo implicito in tipi delegati. Inoltre, alcune espressioni lambda possono essere convertite in modo implicito in tipi di albero delle espressioni. Le conversioni di funzioni anonime sono descritte in modo più dettagliato nelle conversioni di gruppi di metodi e di paragrafo 10.8.
10.2.16 Conversioni letterali predefinite
Esiste una conversione implicita da un default_literal (§12.8.21) a qualsiasi tipo. Questa conversione produce il valore predefinito (§9,3) del tipo dedotto.
10.2.17 Conversioni di throw implicite
Anche se le espressioni throw non hanno un tipo, possono essere convertite in modo implicito in qualsiasi tipo.
10.2.18 Conversione dell'espressione switch
Esiste una conversione implicita da un switch_expression (§12.11) a ogni tipo T per cui esiste una conversione implicita da ogni switch_expression_arm_expression di ogni switch_expression_arm a T.
10.3 Conversioni esplicite
10.3.1 Generale
Le conversioni seguenti vengono classificate come conversioni esplicite:
- Tutte le conversioni implicite (§10.2)
- Conversioni numeriche esplicite (§10.3.2)
- Conversioni esplicite di enumerazione (§10.3.3)
- Conversioni nullable esplicite (§10.3.4)
- Conversioni di tuple esplicite (§10.3.6)
- Conversioni esplicite dei riferimenti (§10.3.5)
- Conversioni esplicite dell'interfaccia
- Conversioni unboxing (§10.3.7)
- Conversioni esplicite dei parametri di tipo (§10.3.8)
- Conversioni esplicite definite dall'utente (§10.3.9)
Le conversioni esplicite possono verificarsi nelle espressioni cast (§12.9.8).
Il set di conversioni esplicite include tutte le conversioni implicite.
Nota: ad esempio, consente l'uso di un cast esplicito quando esiste una conversione implicita di identità, per forzare la selezione di un overload di un metodo specifico. nota finale
Le conversioni esplicite che non sono conversioni implicite sono conversioni che non possono essere dimostrate sempre riuscite, conversioni note probabilmente per perdere informazioni e conversioni tra domini di tipi sufficientemente diversi dal merito di notazione esplicita.
10.3.2 Conversioni numeriche esplicite
Le conversioni numeriche esplicite sono le conversioni da un numeric_type a un'altra numeric_type per cui non esiste già una conversione numerica implicita (§10.2.3):
- Da
sbyteabyte,ushortuint, ,ulongochar. - Da
byteasbyteochar. - Da
shortasbyte,byte,ushortuint,ulong, ochar. - Da
ushortasbyte,byteshort, ochar. - Da
intasbyte,byte,shortushort,uint, ,ulongochar. - Da
uintasbyte,byte,shortushort,int, ochar. - Da
longasbyte,byte,shortushort,int,uint, ,ulongochar. - Da
ulongasbyte,byte,shortushort,int,uint, ,longochar. - Da
charasbyte,byteoshort. - Da
floatasbyte,byte,shortushort,int,uint,long, ,ulong,charodecimal. - Da
doubleasbyte,byteshort,ushort,int,uint,long,ulong, ,char,floatodecimal. - Da
decimalasbyte,byteshort,ushort,int,uint,long,ulong, ,char,floatodouble.
Poiché le conversioni esplicite includono tutte le conversioni numeriche implicite ed esplicite, è sempre possibile eseguire la conversione da qualsiasi numeric_type a qualsiasi altra numeric_type usando un'espressione cast (§12.9.8).
Le conversioni numeriche esplicite potrebbero perdere informazioni o causare la generazione di eccezioni. Una conversione numerica esplicita viene elaborata come segue:
- Per una conversione da un tipo integrale a un altro tipo integrale, l'elaborazione dipende dal contesto di controllo dell'overflow (§12.8.20) in cui viene eseguita la conversione:
- In un
checkedcontesto la conversione ha esito positivo se il valore dell'operando di origine è compreso nell'intervallo del tipo di destinazione, ma genera un'eccezioneSystem.OverflowExceptionse il valore dell'operando di origine non è compreso nell'intervallo del tipo di destinazione. - In un
uncheckedcontesto, la conversione ha sempre esito positivo e procede come indicato di seguito.- Se il tipo di origine è maggiore del tipo di destinazione, il valore di origine viene troncato rimuovendo i bit più significativi "extra". Il risultato viene quindi trattato come un valore del tipo di destinazione.
- Se il tipo di origine è la stessa dimensione del tipo di destinazione, il valore di origine viene considerato come valore del tipo di destinazione
- In un
- Per una conversione da
decimala un tipo integrale, il valore di origine viene arrotondato verso zero al valore integrale più vicino e questo valore integrale diventa il risultato della conversione. Se il valore integrale risultante non è compreso nell'intervallo del tipo di destinazione, viene generata un'eccezioneSystem.OverflowException. - Per una conversione da
floatodoublea un tipo integrale, l'elaborazione dipende dal contesto di controllo dell'overflow (§12.8.20) in cui viene eseguita la conversione:- In un contesto controllato, la conversione procede come segue:
- Se il valore dell'operando è NaN o infinito, viene generata un'eccezione
System.OverflowException. - In caso contrario, l'operando di origine viene arrotondato verso zero al valore integrale più vicino. Se questo valore integrale è compreso nell'intervallo del tipo di destinazione, questo valore è il risultato della conversione.
- In caso contrario viene generata un'eccezione
System.OverflowException.
- Se il valore dell'operando è NaN o infinito, viene generata un'eccezione
- In un contesto deselezionato, la conversione ha sempre esito positivo e procede come indicato di seguito.
- Se il valore dell'operando è NaN o infinito, il risultato della conversione è un valore non specificato del tipo di destinazione.
- In caso contrario, l'operando di origine viene arrotondato verso zero al valore integrale più vicino. Se questo valore integrale è compreso nell'intervallo del tipo di destinazione, questo valore è il risultato della conversione.
- In caso contrario, il risultato della conversione è un valore non specificato del tipo di destinazione.
- In un contesto controllato, la conversione procede come segue:
- Per una conversione da
doubleafloat, ildoublevalore viene arrotondato al valore piùfloatvicino. Se ildoublevalore è troppo piccolo per rappresentare comefloat, il risultato diventa zero con lo stesso segno del valore. Se la grandezza deldoublevalore è troppo grande per rappresentare come ,floatil risultato diventa infinito con lo stesso segno del valore. Se ildoublevalore è NaN, il risultato è anche NaN. - Per una conversione da
floatodoubleindecimal, il valore di origine viene convertito indecimalrappresentazione e arrotondato al numero più vicino, se necessario (§8.3.8).- Se il valore di origine è troppo piccolo per rappresentare come
decimal, il risultato diventa zero, mantenendo il segno del valore originale sedecimalsupporta valori con segno zero. - Se la grandezza del valore di origine è troppo grande per rappresentare come
decimal, o tale valore è infinito, il risultato è infinito mantenendo il segno del valore originale, se la rappresentazione decimale supporta infiniti; in caso contrario, viene generata un'eccezione System.OverflowException. - Se il valore di origine è NaN, il risultato è NaN se la rappresentazione decimale supporta NaN; in caso contrario, viene generata un'eccezione System.OverflowException.
- Se il valore di origine è troppo piccolo per rappresentare come
- Per una conversione da
decimalafloatodouble, ildecimalvalore viene arrotondato al valore odoublepiùfloatvicino. Se la grandezza del valore di origine è troppo grande per rappresentare nel tipo di destinazione o tale valore è infinito, il risultato è infinito mantenendo il segno del valore originale. Se il valore di origine è NaN, il risultato è NaN. Anche se questa conversione può perdere precisione, non genera mai un'eccezione.
Nota: il
decimaltipo non è necessario per supportare valori infiniti o NaN, ma può farlo; il relativo intervallo può essere inferiore all'intervallo difloatedouble, ma non è garantito. Perdecimalle rappresentazioni senza infiniti o valori NaN e con un intervallo minore difloat, il risultato di una conversione dadecimalafloatodoublenon sarà mai infinito o NaN. nota finale
10.3.3 Conversioni esplicite di enumerazione
Le conversioni esplicite di enumerazione sono:
- Da
sbyte,byteshort,ushort,intuintlongulongcharfloat,doubleodecimala qualsiasi enum_type. - Da qualsiasi enum_type a
sbyte,byteshort,ushort, ,int,uint,long,ulongcharfloat,double, o .decimal - Da qualsiasi enum_type a qualsiasi altra enum_type.
Una conversione esplicita dell'enumerazione tra due tipi viene elaborata trattando qualsiasi enum_type partecipante come tipo sottostante di tale enum_type e quindi eseguendo una conversione numerica implicita o esplicita tra i tipi risultanti.
esempio: dato un enum_type
Econ un tipo sottostante diint, una conversione daEabyteviene elaborata come conversione numerica esplicita (§10.3.2) daintabytee una conversione dabyteaEviene elaborata come conversione numerica implicita (§10.2.3) dabyteaint. esempio finale
10.3.4 Conversioni esplicite nullable
Le conversioni nullable esplicite sono quelle conversioni nullable (§10.6.1) derivate da conversioni esplicite e implicite predefinite.
10.3.5 Conversioni esplicite dei riferimenti
Le conversioni di riferimento esplicite sono:
- Dall'oggetto a qualsiasi altro reference_type.
- Da qualsiasi , fornito
Sè una classe base di . - Da qualsiasi , fornito
Snon è sealed e non implementa .T - Da qualsiasi , fornito
Snon è bloccato o viene implementato .T - Da qualsiasi interface_type
Sa qualsiasi interface_typeT, specificatoSnon è derivato daT. - Da un array_type
Scon un tipo diSᵢelemento a un array_typeTcon un tipo diTᵢelemento , purché tutte le condizioni seguenti siano vere:-
SeTdifferiscono solo in tipo di elemento. In altre parole,SeThanno lo stesso numero di dimensioni. - Esiste una conversione di riferimento esplicita da
SᵢaTᵢ.
-
- Da
System.Arraye le interfacce implementate, a qualsiasi array_type. - Da un array_typea
S[],System.Collections.Generic.IList<T>e dalle relative interfacce di base, purché sia presente una conversione di identità o una conversione esplicita dei riferimenti daSystem.Collections.Generic.IReadOnlyList<T>aS. - Da
System.Collections.Generic.IList<S>,System.Collections.Generic.IReadOnlyList<S>e le relative interfacce di base a un tipo diT[]matrice unidimensionale , purché sia presente una conversione di identità o una conversione esplicita dei riferimenti daSa T. - Da
System.Delegatee le interfacce implementate in qualsiasi delegate_type. - Da un tipo riferimento a un tipo
SriferimentoTse ha una conversione di riferimento esplicita daSa un tipoT₀riferimento eT₀viene eseguita una conversione identity daT₀aT. - Da un tipo riferimento a un'interfaccia
So a un tipoTdelegato se è presente una conversione di riferimento esplicita daSa un tipoT₀di interfaccia o delegato edT₀è convertibile in varianza inToTè convertibileT₀a §19.2.3.3. - Da
D<S₁...Sᵥ>aD<T₁...Tᵥ>doveD<X₁...Xᵥ>è un tipo delegato generico,D<S₁...Sᵥ>non è compatibile con o identico aD<T₁...Tᵥ>e per ogni parametroXᵢdi tipo deiDblocchi seguenti:- Se
Xᵢè invariante,Sᵢè identico aTᵢ. - Se
Xᵢè covariante, è presente una conversione di identità, la conversione implicita dei riferimenti o la conversione esplicita dei riferimenti daSᵢaTᵢ. - Se
Xᵢè controvariante,SᵢeTᵢsono identici o entrambi i tipi di riferimento.
- Se
- Conversioni esplicite che coinvolgono parametri di tipo noti come tipi di riferimento. Per altre informazioni sulle conversioni esplicite che coinvolgono parametri di tipo, vedere §10.3.8.
Le conversioni di riferimento esplicite sono quelle conversioni tra reference_typeche richiedono controlli di runtime per assicurarsi che siano corretti.
Affinché una conversione di riferimento esplicita abbia esito positivo in fase di esecuzione, il valore dell'operando di origine deve essere nullo il tipo dell'oggetto a cui fa riferimento l'operando di origine deve essere un tipo che può essere convertito nel tipo di destinazione da una conversione di riferimento implicita (§10.2.8). Se una conversione di riferimento esplicito ha esito negativo, viene generata un'eccezione System.InvalidCastException .
Nota: le conversioni di riferimento, implicite o esplicite, non modificano mai il valore del riferimento stesso (§8.2.1), solo il tipo; né modifica il tipo o il valore dell'oggetto a cui viene fatto riferimento. nota finale
10.3.6 Conversioni di tuple esplicite
Esiste una conversione esplicita da un'espressione E di tupla a un tipo T di tupla se E ha la stessa arità di T e esiste una conversione implicita o esplicita da ogni elemento in E al tipo di elemento corrispondente in .T La conversione viene eseguita creando un'istanza del Ttipo corrispondente System.ValueTuple<...> e inizializzando ognuno dei relativi campi in ordine da sinistra a destra valutando l'espressione dell'elemento tupla corrispondente di E, convertendola nel tipo di elemento corrispondente di T utilizzando la conversione esplicita trovata e inizializzando il campo con il risultato.
10.3.7 Conversioni unboxing
Una conversione unboxing consente di convertire in modo esplicito un reference_type in un value_type. Esistono le conversioni unboxing seguenti:
- Dal tipo
objecta qualsiasi value_type. - Dal tipo
System.ValueTypea qualsiasi value_type. - Dal tipo
System.Enuma qualsiasi enum_type. - Da qualsiasi interface_type a qualsiasi non_nullable_value_type che implementa l'interface_type.
- Da qualsiasi in cui è presente una conversione unboxing da un
Ial di non_nullable_value e una conversione di identità daI₀a . - Da qualsiasi interface_type a qualsiasi
Iin cui è presente una conversione unboxing da un interface_typeI₀al non_nullable_value_type edI₀è variance_convertible aIoIè convertibileI₀in varianza in (§19.2.3.3). - Da qualsiasi reference_type a qualsiasi nullable_value_type in cui è presente una conversione unboxing da reference_type al non_nullable_value_type sottostante del nullable_value_type.
- Da un parametro di tipo che non è noto per essere un tipo valore a qualsiasi tipo in modo che la conversione sia consentita da §10.3.8.
Un'operazione unboxing in un non_nullable_value_type consiste innanzitutto nel verificare che l'istanza dell'oggetto sia un valore boxed del non_nullable_value_type specificato e quindi copiare il valore dall'istanza.
Unboxing in un nullable_value_type produce il valore Null del nullable_value_type se l'operando di origine è nullo il risultato di unboxing dell'istanza dell'oggetto al tipo sottostante del nullable_value_type in caso contrario.
Nota: facendo riferimento alla classe boxing immaginaria descritta in §10.2.9, una conversione unboxing di una casella oggetto in un value_type
Sconsiste nell'eseguire l'espressione((S_Boxing)box).value. Di conseguenza, le istruzioniobject box = new S(); S s = (S)box;concettualmente corrispondono a
object box = new S_Boxing(new S()); S s = ((S_Boxing)box).value;nota finale
Affinché una conversione unboxing in un determinato non_nullable_value_type abbia esito positivo in fase di esecuzione, il valore dell'operando di origine deve essere un riferimento a un valore boxed di tale non_nullable_value_type. Se viene generato l'operando nullSystem.NullReferenceException di origine. Se l'operando di origine è un riferimento a un oggetto incompatibile, viene generata un'eccezione System.InvalidCastException .
Affinché una conversione unboxing in un determinato nullable_value_type abbia esito positivo in fase di esecuzione, il valore dell'operando di origine deve essere Null o un riferimento a un valore boxed del non_nullable_value_type sottostante del nullable_value_type. Se l'operando di origine è un riferimento a un oggetto incompatibile, viene generata un'eccezione System.InvalidCastException .
10.3.8 Conversioni esplicite che coinvolgono parametri di tipo
Per un noto come tipo riferimento (T), esistono le conversioni di riferimento esplicite seguenti (§10.3.5):
- Dalla classe
Cbase effettiva diTaTe da qualsiasi classe di base diCaT. - Da qualsiasi interface_type a
T. - Da
Ta qualsiasi interface_typeIpurché non sia già presente una conversione di riferimento implicita daTaI. - Da un a
Ucondizione cheTdipende daT(U).Nota: poiché
Tè noto come tipo riferimento, nell'ambito diT, il tipo di runtime di sarà sempre un tipo riferimento, anche seUnon è noto come tipo riferimento in fase di compilazione. nota finale
Per un che nonTnoto come tipo di riferimento (§15.2.5), le conversioni seguenti che coinvolgono sono considerate conversioni unboxing (T) in fase di compilazione. In fase di esecuzione, se T è un tipo di valore, la conversione viene eseguita come conversione unboxing. In fase di esecuzione, se T è un tipo di riferimento, la conversione viene eseguita come conversione esplicita dei riferimenti o conversione di identità.
- Dalla classe
Cbase effettiva diTaTe da qualsiasi classe di base diCaT.Nota: C sarà uno dei tipi
System.Object,System.ValueTypeoSystem.Enum(in caso contrarioTsarebbe noto come tipo riferimento). nota finale - Da qualsiasi interface_type a
T.
Per un type_parameterT che non è noto come tipo riferimento (§15.2.5), esistono le conversioni esplicite seguenti:
- Da
Ta qualsiasi interface_typeIfornito non esiste già una conversione implicita daTaI. Questa conversione è costituita da una conversione boxing implicita (§10.2.9) daTaobjectseguita da una conversione di riferimento esplicita daobjectaI. In fase di esecuzione, seTè un tipo di valore, la conversione viene eseguita come conversione boxing seguita da una conversione di riferimento esplicita. In fase di esecuzione, seTè un tipo riferimento, la conversione viene eseguita come conversione di riferimento esplicita. - Da un parametro
Udi tipo aTspecificato cheTdipende daU(§15.2.5). In fase di esecuzione, seTè un tipo valore eUè un tipo riferimento, la conversione viene eseguita come conversione unboxing. In fase di esecuzione, se eTUsono tipi valore,TeUsono necessariamente lo stesso tipo e non viene eseguita alcuna conversione. In fase di esecuzione, seTè un tipo riferimento,Uè necessariamente anche un tipo riferimento e la conversione viene eseguita come conversione esplicita dei riferimenti o conversione di identità.
In tutti i casi, le regole assicurano che una conversione venga eseguita come conversione unboxing se e solo se in fase di esecuzione la conversione proviene da un tipo riferimento a un tipo valore.
Le regole precedenti non consentono una conversione esplicita diretta da un parametro di tipo non vincolato a un tipo non di interfaccia, che potrebbe essere sorprendente. Il motivo di questa regola è evitare confusione e rendere chiara la semantica di tali conversioni.
Esempio: si consideri la dichiarazione seguente:
class X<T> { public static long F(T t) { return (long)t; // Error } }Se la conversione esplicita diretta di
tinlongè consentita, si potrebbe facilmente aspettarsi cheX<int>.F(7)restituisca7L. Tuttavia, non lo sarebbe, perché le conversioni numeriche standard vengono considerate solo quando i tipi sono noti come numerici in fase di associazione. Per rendere chiara la semantica, l'esempio precedente deve invece essere scritto:class X<T> { public static long F(T t) { return (long)(object)t; // Ok, but will only work when T is long } }Questo codice verrà ora compilato ma in esecuzione
X<int>.F(7)genererà quindi un'eccezione in fase di esecuzione, poiché un boxedintnon può essere convertito direttamente in un oggettolong.esempio finale
10.3.9 Conversioni esplicite definite dall'utente
Una conversione esplicita definita dall'utente è costituita da una conversione esplicita standard facoltativa, seguita dall'esecuzione di un operatore di conversione implicito o esplicito definito dall'utente, seguito da un'altra conversione esplicita standard facoltativa. Le regole esatte per la valutazione delle conversioni esplicite definite dall'utente sono descritte in §10.5.5.
10.4 Conversioni standard
10.4.1 Generale
Le conversioni standard sono quelle conversioni predefinite che possono verificarsi come parte di una conversione definita dall'utente.
10.4.2 Conversioni implicite standard
Le conversioni implicite seguenti vengono classificate come conversioni implicite standard:
- Conversioni di identità (§10.2.2)
- Conversioni numeriche implicite (§10.2.3)
- Conversioni nullable implicite (§10.2.6)
- Conversioni di valori letterali Null (§10.2.7)
- Conversioni di riferimenti implicite (§10.2.8)
- Conversioni boxing (§10.2.9)
- Conversioni implicite di espressioni costanti (§10.2.11)
- Conversioni implicite che coinvolgono parametri di tipo (§10.2.12)
Le conversioni implicite standard escludono in modo specifico conversioni implicite definite dall'utente.
10.4.3 Conversioni esplicite standard
Le conversioni esplicite standard sono tutte conversioni implicite standard più il subset delle conversioni esplicite per le quali esiste una conversione implicita standard opposta.
Nota: se esiste una conversione implicita standard da un tipo a un tipo
AB, esiste una conversione esplicita standard dal tipoAal tipoBe dal tipoBal tipoA. nota finale
10.5 Conversioni definite dall'utente
10.5.1 Generale
C# consente l'aumento delle conversioni implicite ed esplicite predefinite tramite conversioni definite dall'utente. Le conversioni definite dall'utente vengono introdotte dichiarando gli operatori di conversione (§15.10.4) nei tipi di classe e struct.
10.5.2 Conversioni definite dall'utente consentite
C# consente di dichiarare solo determinate conversioni definite dall'utente. In particolare, non è possibile ridefinire una conversione implicita o esplicita già esistente.
Per un tipo di origine e un tipo S di Tdestinazione specificati, se S o T sono tipi valore nullable, consentire S₀ e T₀ fare riferimento ai relativi tipi sottostanti, in caso contrario S₀ e T₀ sono uguali rispettivamente a S e T . Una classe o uno struct è autorizzato a dichiarare una conversione da un tipo di origine a un tipo S di T destinazione solo se sono soddisfatte tutte le condizioni seguenti:
-
S₀eT₀sono tipi diversi. -
S₀OppureT₀è il tipo di classe o struct in cui viene eseguita la dichiarazione dell'operatore. - Né
S₀néT₀è un interface_type. - Escluse le conversioni definite dall'utente, una conversione non esiste da
SaTo daTaS.
Le restrizioni applicabili alle conversioni definite dall'utente sono specificate in §15.10.4.
10.5.3 Valutazione delle conversioni definite dall'utente
Una conversione definita dall'utente converte un'espressione di origine, che può avere un tipo di origine, in un altro tipo, denominato tipo di destinazione. Valutazione di un centro di conversione definito dall'utente per trovare l'operatore di conversione definito dall'utente più specifico per l'espressione di origine e il tipo di destinazione. Questa determinazione è suddivisa in diversi passaggi:
- Ricerca del set di classi e struct da cui verranno considerati gli operatori di conversione definiti dall'utente. Questo set è costituito dal tipo di origine e dalle relative classi base, se il tipo di origine esiste, insieme al tipo di destinazione e alle relative classi di base. A questo scopo si presuppone che solo le classi e gli struct possano dichiarare operatori definiti dall'utente e che i tipi non di classe non abbiano classi di base. Inoltre, se il tipo di origine o di destinazione è un tipo nullable-value-type, viene invece usato il tipo sottostante.
- Da tale set di tipi, determinare quali operatori di conversione definiti dall'utente e lifted sono applicabili. Affinché un operatore di conversione sia applicabile, è possibile eseguire una conversione standard (§10.4) dall'espressione di origine al tipo di operando dell'operatore ed è possibile eseguire una conversione standard dal tipo di risultato dell'operatore al tipo di destinazione.
- Dal set di operatori definiti dall'utente applicabili, determinare l'operatore senza ambiguità più specifico. In generale, l'operatore più specifico è l'operatore il cui tipo di operando è "più vicino" all'espressione di origine e il cui tipo di risultato è "più vicino" al tipo di destinazione. Gli operatori di conversione definiti dall'utente sono preferiti rispetto agli operatori di conversione lifted. Le regole esatte per stabilire l'operatore di conversione definito dall'utente più specifico sono definite nelle sottoclause seguenti.
Dopo aver identificato un operatore di conversione definito dall'utente più specifico, l'esecuzione effettiva della conversione definita dall'utente prevede fino a tre passaggi:
- Prima di tutto, se necessario, eseguire una conversione standard dall'espressione di origine al tipo di operando dell'operatore di conversione definito dall'utente o lifted.
- Richiamare quindi l'operatore di conversione lifted o definito dall'utente per eseguire la conversione.
- Infine, se necessario, eseguire una conversione standard dal tipo di risultato dell'operatore di conversione definito dall'utente al tipo di destinazione.
La valutazione di una conversione definita dall'utente non comporta mai più di un operatore di conversione definito dall'utente o lifted. In altre parole, una conversione dal tipo al tipo ST non eseguirà mai una conversione definita dall'utente da a S e quindi eseguirà una conversione definita dall'utente da XX a T.
- Le definizioni esatte della valutazione delle conversioni implicite o esplicite definite dall'utente vengono fornite nelle sottoclause seguenti. Le definizioni usano i termini seguenti:
- Se esiste una conversione implicita standard (§10.4.2) da un tipo
Aa un tipoB, e se nonAnéBsono interface_type,Aviene detto che incluso daBeBviene detto includereA. - Se esiste una conversione implicita standard (§10.4.2) da un'espressione
Ea un tipoB, e se nonBné il tipo diE(se ne dispone) sono interface_types,Eviene detto che incluso daBeBviene detto che includereE. - Il tipo più incomprensivo in un set di tipi è quello che include tutti gli altri tipi nel set. Se nessun singolo tipo include tutti gli altri tipi, il set non ha alcun tipo più incomprensivo. In termini più intuitivi, il tipo più incomprensibile è il tipo "più grande" nel set, ovvero quello in cui ognuno degli altri tipi può essere convertito in modo implicito.
- Il tipo più incluso in un set di tipi è quello incluso in tutti gli altri tipi del set. Se nessun singolo tipo è incluso in tutti gli altri tipi, il set non ha alcun tipo più incluso. In termini più intuitivi, il tipo più incluso è il tipo "più piccolo" nel set, ovvero quello che può essere convertito in modo implicito in ognuno degli altri tipi.
10.5.4 Conversioni implicite definite dall'utente
Una conversione implicita definita dall'utente da un'espressione E a un tipo T viene elaborata come segue:
Determinare i tipi
SeS₀T₀.- Se
Eha un tipo, lasciareSche sia quel tipo. - Se
SoTsono tipi valore nullable, lasciareSᵢeTᵢessere i relativi tipi sottostanti, in caso contrario lasciareSᵢeTᵢessereSrispettivamente eT. - Se
SᵢoTᵢsono parametri di tipo, lasciareS₀eT₀essere le relative classi di base valide, in caso contrario lasciareS₀eT₀essereSᵢrispettivamente eTᵢ.
- Se
Trovare il set di tipi,
D, da cui verranno considerati gli operatori di conversione definiti dall'utente. Questo set è costituito daS₀(se esistente ed è una classe o uno struct), dalle classi di base diS₀(seS₀S₀esistente ed è una classe) eT₀(seT₀è una classe o uno struct). Al setDviene aggiunto un tipo solo se non esiste una conversione di identità in un altro tipo già incluso nel set.Trovare il set di operatori di conversione definiti dall'utente e lifted applicabili,
U. Questo set è costituito dagli operatori di conversione impliciti definiti dall'utente e lifted dichiarati dalle classi o dagli struct inDche converte da un tipo che includeEa un tipo incluso daT. SeUè vuoto, la conversione non è definita e si verifica un errore in fase di compilazione.Trovare il tipo di origine più specifico,
Sₓ, degli operatori inU:- Se
Sesiste e uno degli operatori nellaUconversione daS,SₓèS. - In caso contrario,
Sₓè il tipo più incluso nel set combinato di tipi di origine degli operatori inU. Se non è possibile trovare esattamente un tipo più incluso, la conversione è ambigua e si verifica un errore in fase di compilazione.
- Se
Trovare il tipo di destinazione più specifico,
Tₓ, degli operatori inU:- Se uno degli operatori in converte in
UT,TₓèT. - In caso contrario,
Tₓè il tipo più incluso nel set combinato di tipi di destinazione degli operatori inU. Se non è possibile trovare esattamente uno dei tipi più inclusi, la conversione è ambigua e si verifica un errore in fase di compilazione.
- Se uno degli operatori in converte in
Trovare l'operatore di conversione più specifico:
- Se
Ucontiene esattamente un operatore di conversione definito dall'utente che esegue la conversione daSₓaTₓ, questo è l'operatore di conversione più specifico. - In caso contrario, se
Ucontiene esattamente un operatore di conversione lifted che converte daSₓaTₓ, questo è l'operatore di conversione più specifico. - In caso contrario, la conversione è ambigua e si verifica un errore in fase di compilazione.
- Se
Infine, applicare la conversione:
- Se E non dispone già del tipo
Sₓ, viene eseguita una conversione implicita standard daEaSₓ. - L'operatore di conversione più specifico viene richiamato per eseguire la conversione da
SₓaTₓ. - Se
TₓnonTè , viene eseguita una conversione implicita standard daTₓaT.
- Se E non dispone già del tipo
Esiste una conversione implicita definita dall'utente da un tipo S a un tipo T se esiste una conversione implicita definita dall'utente da una variabile di tipo S a T.
10.5.5 Conversioni esplicite definite dall'utente
Una conversione esplicita definita dall'utente da un'espressione E a un tipo T viene elaborata come segue:
- Determinare i tipi
SeS₀T₀.- Se
Eha un tipo, lasciareSche sia quel tipo. - Se
SoTsono tipi valore nullable, lasciareSᵢeTᵢessere i relativi tipi sottostanti, in caso contrario lasciareSᵢeTᵢessereSrispettivamente eT. - Se
SᵢoTᵢsono parametri di tipo, lasciareS₀eT₀essere le relative classi di base valide, in caso contrario lasciareS₀eT₀essereSᵢrispettivamente eTᵢ.
- Se
- Trovare il set di tipi,
D, da cui verranno considerati gli operatori di conversione definiti dall'utente. Questo set è costituito daS₀(seS₀esistente ed è una classe o uno struct), dalle classi di base diS₀(seS₀esistente ed è una classe),T₀(seT₀è una classe o uno struct) e dalle classi base diT₀(seT₀è una classe). Al setDviene aggiunto un tipo solo se non esiste una conversione di identità in un altro tipo già incluso nel set. - Trovare il set di operatori di conversione definiti dall'utente e lifted applicabili,
U. Questo set è costituito dagli operatori di conversione impliciti o espliciti definiti dall'utente dichiarati dalle classi o dagli struct inDche converte da un tipo che includeEo include (Sse esistente) a un tipo che include o include .TSeUè vuoto, la conversione non è definita e si verifica un errore in fase di compilazione. - Trovare il tipo di origine più specifico,
Sₓ, degli operatori inU:- Se S esiste e uno degli operatori in
Uconvert daS,SₓèS. - In caso contrario, se uno degli operatori in
Uconverte da tipi che includonoE,Sₓè il tipo più incluso nel set combinato di tipi di origine di tali operatori. Se non è possibile trovare alcun tipo più incluso, la conversione è ambigua e si verifica un errore in fase di compilazione. - In caso contrario,
Sₓè il tipo più incluso nel set combinato di tipi di origine degli operatori inU. Se non è possibile trovare esattamente uno dei tipi più inclusi, la conversione è ambigua e si verifica un errore in fase di compilazione.
- Se S esiste e uno degli operatori in
- Trovare il tipo di destinazione più specifico,
Tₓ, degli operatori inU:- Se uno degli operatori in converte in
UT,TₓèT. - In caso contrario, se uno degli operatori in
Uconverte in tipi inclusi inT,Tₓè il tipo più incluso nel set combinato di tipi di destinazione di tali operatori. Se non è possibile trovare esattamente uno dei tipi più inclusi, la conversione è ambigua e si verifica un errore in fase di compilazione. - In caso contrario,
Tₓè il tipo più incluso nel set combinato di tipi di destinazione degli operatori inU. Se non è possibile trovare alcun tipo più incluso, la conversione è ambigua e si verifica un errore in fase di compilazione.
- Se uno degli operatori in converte in
- Trovare l'operatore di conversione più specifico:
- Se U contiene esattamente un operatore di conversione definito dall'utente che esegue la conversione da
SₓaTₓ, questo è l'operatore di conversione più specifico. - In caso contrario, se
Ucontiene esattamente un operatore di conversione lifted che converte daSₓaTₓ, questo è l'operatore di conversione più specifico. - In caso contrario, la conversione è ambigua e si verifica un errore in fase di compilazione.
- Se U contiene esattamente un operatore di conversione definito dall'utente che esegue la conversione da
- Infine, applicare la conversione:
- Se
Enon ha già il tipoSₓ, viene eseguita una conversione esplicita standard da E aSₓ. - L'operatore di conversione definito dall'utente più specifico viene richiamato per eseguire la conversione da
SₓaTₓ. - Se
TₓnonTè , viene eseguita una conversione esplicita standard daTₓaT.
- Se
Esiste una conversione esplicita definita dall'utente da un tipo S a un tipo T se esiste una conversione esplicita definita dall'utente da una variabile di tipo S a T.
10.6 Conversioni che coinvolgono tipi nullable
10.6.1 Conversioni nullable
Una conversione nullable consente di utilizzare anche una conversione predefinita che opera su un tipo di valore non nullable con il formato nullable di tale tipo. Per ognuna delle conversioni implicite o esplicite predefinite che vengono convertite da un tipo di valore non nullable a un tipo S di T valore non nullable (§10.2.2, §10.2.3, §10.2.4, §10.2.11, §10.3.2 e §10.3.3), esistono le conversioni nullable seguenti:
- Conversione implicita o esplicita da
S?aT? - Conversione implicita o esplicita da
SaT? - Conversione esplicita da
S?aT.
Una conversione nullable viene classificata come conversione implicita o esplicita.
Alcune conversioni nullable vengono classificate come conversioni standard e possono verificarsi come parte di una conversione definita dall'utente. In particolare, tutte le conversioni implicite nullable vengono classificate come conversioni implicite standard (§10.4.2) e quelle esplicite nullable che soddisfano i requisiti di §10.4.3 vengono classificate come conversioni esplicite standard.
Valutazione di una conversione nullable basata su una conversione sottostante da S a T procede come indicato di seguito:
- Se la conversione nullable è da
S?aT?:- Se il valore di origine è null (
HasValuela proprietà èfalse), il risultato è il valore Null di tipoT?. - In caso contrario, la conversione viene valutata come un annullamento del wrapping da
S?aS, seguita dalla conversione sottostante da aS, seguita da un wrapping daTTaT?.
- Se il valore di origine è null (
- Se la conversione nullable è da
SaT?, la conversione viene valutata come conversione sottostante daSaTseguita da un wrapping daTaT?. - Se la conversione nullable è da
S?aT, la conversione viene valutata come un annullamento del wrapping daS?aSseguito della conversione sottostante daSaT.
10.6.2 Conversioni lifted
Dato un operatore di conversione definito dall'utente che esegue la conversione da un tipo di S valore non nullable a un tipo valore Tnon nullable, esiste un operatore di conversione lifted che converte da S? a T?. Questo operatore di conversione lifted esegue un unwrapping da S? a S seguito della conversione definita dall'utente da a S seguito di un wrapping da TT a T?, ad eccezione del fatto che un valore S? Null converte direttamente in un valore T?Null. Un operatore di conversione lifted ha la stessa classificazione implicita o esplicita dell'operatore di conversione definito dall'utente sottostante.
10.7 Conversioni di funzioni anonime
10.7.1 Generale
Un anonymous_method_expression o un lambda_expression è classificato come funzione anonima (§12.21). L'espressione non dispone di un tipo, ma può essere convertita in modo implicito in un tipo delegato compatibile. Alcune espressioni lambda possono anche essere convertite in modo implicito in un tipo di albero delle espressioni compatibile.
In particolare, una funzione F anonima è compatibile con un tipo D delegato fornito:
- Se
Fcontiene un anonymous_function_signature,DeFavere lo stesso numero di parametri. - Se
Fnon contiene un anonymous_function_signature,Dpuò avere zero o più parametri di qualsiasi tipo, purché nessun parametro di sia un parametro diDoutput. - Se
Fha un elenco di parametri tipizzato in modo esplicito, ogni parametro inDha gli stessi modificatori del parametro corrispondente inFe esiste una conversione identity tra il parametro corrispondente inF. - Se
Fha un elenco di parametri tipizzato in modo implicito,Dnon dispone di parametri di riferimento o di output. - Se il corpo di
Fè un'espressione eDha un tipo di ritorno void oFè asincrono eDha un tipo di ritorno«TaskType»(§15.14.1), quando a ogni parametro diFviene assegnato il tipo del parametro corrispondente inD, il corpo diFè considerato un'espressione valida (w.r.t §12) che è consentita come statement_expression (§13.7). - Se il corpo di
Fè un blocco e ha un tipoDè asincrono eFha unDtipo restituito , quando a ogni parametro di«TaskType»viene assegnato il tipo del parametro corrispondente inF, il corpo diDè un blocco valido (w.r.tF) in cui nessuna istruzione specifica un'espressione. - Se il corpo di
Fè un'espressione eFnon è asincrono eDha un tipo nonvoidrestituitoTè asincrono eFha unDtipo«TaskType»<T>restituito (§15.14.1), quando a ogni parametro vieneFassegnato il tipo del parametro corrispondente in , il corpo diDè un'espressione valida (w.r.tF) che è implicitamente convertibile inT. - Se il corpo di
Fè un blocco eFnon è asincrono eDha un tipo restituito non void oppureTrestituito, quando a ogni parametro vieneFassegnato il tipo del parametro corrispondente inD, il corpo di è un blocco di«TaskType»<T>istruzioni valido (w.r.tF) con un punto finale non raggiungibile in cui ogni istruzione return specifica un'espressione che è implicitamente convertibileDin .
Esempio: gli esempi seguenti illustrano queste regole:
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"; };esempio finale
Esempio: gli esempi seguenti usano un tipo
Func<A,R>delegato generico che rappresenta una funzione che accetta un argomento di tipo e restituisce un valore di tipoAR:delegate R Func<A,R>(A arg);Nelle assegnazioni
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; // OkIl parametro e i tipi restituiti di ogni funzione anonima vengono determinati dal tipo della variabile a cui viene assegnata la funzione anonima.
La prima assegnazione converte correttamente la funzione anonima nel tipo
Func<int,int>delegato perché, quandoxviene specificato il tipo ,intè un'espressione valida convertibile in modo implicito nel tipox + 1int.Analogamente, la seconda assegnazione converte correttamente la funzione anonima nel tipo delegato Func<int, double> perché il risultato di
x + 1(di tipoint) è convertibile in modo implicito nel tipodouble.Tuttavia, la terza assegnazione è un errore in fase di compilazione perché, quando
xviene specificato il tipodouble, il risultato dix + 1(di tipodouble) non è convertibile in modo implicito nel tipoint.La quarta assegnazione converte correttamente la funzione asincrona anonima nel tipo
Func<int, Task<int>>delegato perché il risultato dix + 1(di tipoint) è convertibile in modo implicito nel tipointrestituito effettivo dell'espressione lambda asincrona, che ha un tipoTask<int>restituito .esempio finale
Un'espressione F lambda è compatibile con un tipo di Expression<D> albero delle espressioni se F è compatibile con il tipo Ddelegato . Ciò non si applica ai metodi anonimi, ma solo alle espressioni lambda.
Le funzioni anonime possono influenzare la risoluzione dell'overload e partecipare all'inferenza del tipo. Per altri dettagli, vedere §12.6 .
10.7.2 Valutazione delle conversioni di funzioni anonime in tipi delegati
La conversione di una funzione anonima in un tipo delegato produce un'istanza del delegato che fa riferimento alla funzione anonima e al set (possibilmente vuoto) di variabili esterne acquisite attive al momento della valutazione. Quando viene richiamato il delegato, viene eseguito il corpo della funzione anonima. Il codice nel corpo viene eseguito usando il set di variabili esterne acquisite a cui fa riferimento il delegato. Un delegate_creation_expression (§12.8.17.5) può essere utilizzato come sintassi alternativa per convertire un metodo anonimo in un tipo delegato.
L'elenco chiamate di un delegato generato da una funzione anonima contiene una singola voce. L'oggetto di destinazione esatto e il metodo di destinazione del delegato non sono specificati. In particolare, non è specificato se l'oggetto di destinazione del delegato è null, il this valore del membro della funzione di inclusione o un altro oggetto.
Le conversioni di funzioni anonime identiche semanticamente con lo stesso set (possibilmente vuoto) di istanze di variabili esterne acquisite negli stessi tipi delegati sono consentite (ma non necessarie) di restituire la stessa istanza del delegato. Il termine semanticamente identico viene usato qui per indicare che l'esecuzione delle funzioni anonime, in tutti i casi, produce gli stessi effetti in base agli stessi argomenti. Questa regola consente l'ottimizzazione del codice, ad esempio il codice seguente.
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));
...
}
}
Poiché i due delegati di funzione anonima hanno lo stesso set (vuoto) di variabili dell'ambiente esterno acquisite e poiché le funzioni anonime sono semanticamente identiche, ai delegati è permesso di riferirsi allo stesso metodo di destinazione. In effetti, un compilatore può restituire la stessa istanza del delegato da entrambe le espressioni di funzione anonime.
10.7.3 Valutazione delle conversioni di espressioni lambda in tipi di albero delle espressioni
La conversione di un'espressione lambda in un tipo di albero delle espressioni produce un albero delle espressioni (§8.6). Più precisamente, la valutazione della conversione dell'espressione lambda produce una struttura oggetto che rappresenta la struttura dell'espressione lambda stessa.
Non tutte le espressioni lambda possono essere convertite in tipi di albero delle espressioni. La conversione in un tipo delegato compatibile esiste sempre, ma può non riuscire in fase di compilazione per motivi definiti dall'implementazione.
Nota: i motivi comuni per cui un'espressione lambda non riesce a eseguire la conversione in un tipo di albero delle espressioni includono:
- Ha un corpo del blocco
- Ha il
asyncmodificatore- Contiene un operatore di assegnazione
- Contiene un parametro di output o riferimento
- Contiene un'espressione associata dinamicamente
nota finale
Conversioni dei gruppi di metodi 10.8
Esiste una conversione implicita da un gruppo di metodi (§12.2) a un tipo delegato compatibile (§21.4). Se D è un tipo delegato ed E è un'espressione classificata come gruppo di metodi, D è compatibile con E se e solo se E contiene almeno un metodo applicabile nel formato normale (§12.6.4.2) a qualsiasi elenco di argomenti (§12.6.2) con tipi e modificatori corrispondenti ai tipi e modificatori di D, come descritto di seguito.
L'applicazione in fase di compilazione della conversione da un gruppo E di metodi a un tipo D delegato è descritta di seguito.
- Viene selezionato un singolo metodo
Mcorrispondente a una chiamata al metodo (§12.8.10.2) del moduloE(A), con le modifiche seguenti:- L'elenco
Adi argomenti è un elenco di espressioni, ognuna classificata come variabile e con il tipo e il modificatore (in,outoref) del parametro corrispondente nella parameter_list di , ad eccezione deiDparametri di tipodynamic, dove l'espressione corrispondente ha il tipoobjectanzichédynamic. - I metodi candidati considerati sono solo i metodi applicabili nella forma normale e non omettono parametri facoltativi (§12.6.4.2). Pertanto, i metodi candidati vengono ignorati se sono applicabili solo nel formato espanso o se uno o più dei relativi parametri facoltativi non dispongono di un parametro corrispondente in
D.
- L'elenco
- Una conversione viene considerata esistente se l'algoritmo di §12.8.10.2 produce un singolo metodo
Mmigliore compatibile (§21.4) conD. - Se il metodo selezionato è un metodo
Mdi istanza, l'espressione di istanza associata aEdetermina l'oggetto di destinazione del delegato. - Se il metodo selezionato è un metodo
Mdi estensione indicato tramite l'accesso a un membro in un'espressione di istanza, tale espressione di istanza determina l'oggetto di destinazione del delegato. - Il risultato della conversione è un valore di tipo
D, ovvero un delegato che fa riferimento al metodo selezionato e all'oggetto di destinazione.
Esempio: di seguito vengono illustrate le conversioni dei gruppi di metodi:
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 } }L'assegnazione a
d1converte in modo implicito il gruppoFdi metodi in un valore di tipoD1.L'assegnazione a
d2mostra come è possibile creare un delegato a un metodo con tipi di parametro meno derivati (controvarianti) e un tipo restituito più derivato (covariante).L'assegnazione a
d3mostra come non esiste alcuna conversione se il metodo non è applicabile.L'assegnazione a
d4mostra come il metodo deve essere applicabile nella forma normale.L'assegnazione a
d5mostra come i tipi di parametro e restituiti del delegato e del metodo possono differire solo per i tipi di riferimento.esempio finale
Come per tutte le altre conversioni implicite ed esplicite, l'operatore cast può essere usato per eseguire in modo esplicito una particolare conversione.
Esempio: di conseguenza, l'esempio
object obj = new EventHandler(myDialog.OkClick);potrebbe invece essere scritto
object obj = (EventHandler)myDialog.OkClick;esempio finale
Una conversione di un gruppo di metodi può fare riferimento a un metodo generico, specificando in modo esplicito argomenti di tipo all'interno Edi o tramite inferenza del tipo (§12.6.3). Se si usa l'inferenza del tipo, i tipi di parametro del delegato vengono usati come tipi di argomento nel processo di inferenza. Il tipo restituito del delegato non viene usato per l'inferenza. Se gli argomenti di tipo vengono specificati o dedotti, fanno parte del processo di conversione del gruppo di metodi; si tratta degli argomenti di tipo utilizzati per richiamare il metodo di destinazione quando viene richiamato il delegato risultante.
Esempio:
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 } }esempio finale
I gruppi di metodi possono influenzare la risoluzione dell'overload e partecipare all'inferenza dei tipi. Per altri dettagli, vedere §12.6 .
La valutazione in fase di esecuzione di una conversione di un gruppo di metodi procede come segue:
- Se il metodo selezionato in fase di compilazione è un metodo di istanza o è un metodo di estensione a cui si accede come metodo di istanza, l'oggetto di destinazione del delegato viene determinato dall'espressione di istanza associata a
E:- L'espressione di istanza viene valutata. Se questa valutazione causa un'eccezione, non vengono eseguiti altri passaggi.
- Se l'espressione di istanza è di un reference_type, il valore calcolato dall'espressione di istanza diventa l'oggetto di destinazione. Se il metodo selezionato è un metodo di istanza e l'oggetto di destinazione è
null, viene generata un'eccezioneSystem.NullReferenceExceptione non vengono eseguiti altri passaggi. - Se l'espressione di istanza è di un value_type, viene eseguita un'operazione boxing (§10.2.9) per convertire il valore in un oggetto e questo oggetto diventa l'oggetto di destinazione.
- In caso contrario, il metodo selezionato fa parte di una chiamata al metodo statico e l'oggetto di destinazione del delegato è
null. - Un'istanza del delegato di tipo
Ddelegato viene ottenuta con un riferimento al metodo determinato in fase di compilazione e un riferimento all'oggetto di destinazione calcolato in precedenza, come indicato di seguito:- La conversione è consentita (ma non necessaria) per usare un'istanza di delegato esistente che contiene già questi riferimenti.
- Se un'istanza esistente non è stata riutilizzata, ne viene creata una nuova (§21.5). Se non è disponibile memoria sufficiente per allocare la nuova istanza, viene generata un'eccezione
System.OutOfMemoryException. In caso contrario, l'istanza viene inizializzata con i riferimenti specificati.
ECMA C# draft specification