2009-05-13 4 views
204

Ho appena imbattuto in un errore di strano:Nullable types e l'operatore ternario: perché è `? 10: null` vietato?

private bool GetBoolValue() 
{ 
    //Do some logic and return true or false 
} 

Poi, in un altro metodo, qualcosa di simile:

int? x = GetBoolValue() ? 10 : null; 

Semplice, se il metodo restituisce true, assegnare 10 alle Nullable int x. Altrimenti, assegna null al valore n. int. int. Tuttavia, il compilatore si lamenta:

Error 1 Type of conditional expression cannot be determined because there is no implicit conversion between int and <null> .

Sono impazzito?

+2

Forse questo sarà corretto in una versione futura del compilatore, perché, non c'è davvero una conversione implicita tra 'int' e '' ed è 'int?' –

+0

@bruno, Com'è diverso dal dire "esiste davvero una conversione implicita tra [qualsiasi tipo] e [qualsiasi altro tipo] e questo è" oggetto ""? Pensi che lo "correggeranno" anche in una versione futura del compilatore? Ad esempio: oggetto x = GetBoolValue()? (oggetto) "pippo": (oggetto) DateTime.Now; – LukeH

+2

Immaginate che il compilatore fosse abbastanza intelligente da dire "OK, non stiamo usando il primo tipo (int), quindi cercheremo un altro tipo che possiamo usare per il risultato di questa espressione." Cosa otterresti se hai digitato "Console.WriteLine ((Predicate()? 5.6: null) .GetType(). ToString());"? Vuoi prendere il galleggiante? o doppio? o oggetto? O una classe definita dall'utente con conversione implicita da float? Come fa il compilatore a sapere quale tipo scegliere, dal momento che non può scegliere quello nell'espressione? – mquander

risposta

336

Il compilatore cerca prima di valutare l'espressione a destra:

GetBoolValue() ? 10 : null 

Il 10 è un int letterale (non int?) e null è , beh, null. Non c'è conversione implicita tra questi due quindi il messaggio di errore.

Se si modifica l'espressione di destra per una delle seguenti allora compila perché non v'è una conversione implicita tra int? e null (# 1) e tra int e int? (# 2, # 3).

GetBoolValue() ? (int?)10 : null // #1 
GetBoolValue() ? 10 : (int?)null // #2 
GetBoolValue() ? 10 : default(int?) // #3 
+6

Puoi anche scrivere 'new int?()'. – SLaks

+36

O ancora meglio IMHO: 'default (int?)' – Lucero

+1

Sì, il compilatore richiede questo indizio prima che ** avvolga ** il 'int' 10 in un' int? '. Si noti che un'altra possibilità sarebbe quella di ** box ** il 'int' 10 in una classe base o interfaccia di' System.Int32'. Ad esempio 'GetBoolValue()? (IFormattable) 10: null // # 1B' o 'GetBoolValue()? 10: (IFormattable) null // # 2B' sarà OK. Questa possibilità potrebbe essere una ragione per cui non rendono automatico il nullable-wrapping. Poiché sia ​​il wrapping che il boxing sono normalmente conversioni _intlusive. Questo è sia "int? variable = 10; 'e' IFormattable variable = 10; 'sono legali (il precedente avvolge, le ultime caselle). –

30

Prova questo:

int? x = GetBoolValue() ? 10 : (int?)null; 

Fondamentalmente ciò che sta accadendo è che operatore condizionale è in grado di determinare il "tipo di ritorno" dell'espressione. Poiché il compilatore decide implicitamente che 10 è un int, decide quindi che il tipo restituito di questa espressione deve essere anche int. Poiché uno int non può essere null (il terzo operando dell'operatore condizionale) si lamenta.

Trasmettendo lo null a Nullable<int>, stiamo dicendo esplicitamente al compilatore che il tipo restituito di questa espressione deve essere un Nullable<int>. Si potrebbe avere altrettanto facilmente il 10 a int? e ha avuto lo stesso effetto.

+0

So che posso aggirarlo, sono solo curioso di sapere perché questo sta accadendo? – BFree

3
int? x = GetBoolValue() ? 10 : (int?)null; 

La ragione per cui si vede questo è dovuto al fatto dietro le quinte si sta utilizzando Nullable e avete bisogno di dire a C# che il "null" è un'istanza nulla di Nullable.

+0

Doh! Andrew ha ottenuto il primo. +1 a lui. –

4

provare uno di questi:

int? x = GetBoolValue() ? (int?)10 : null; 

int? x = GetBoolValue() ? 10 : (int?)null; 
+0

possiamo aggiungerne un altro: int? x = GetBoolValue()? 10: new int?(); – Eniola

3

basta aggiungere un cast explict.

int? x = GetBoolValue() ? 10 : (int?)null; 

E è l'operatore ternario che si confonde - il secondo argomento è un intero ed è così il terzo argomento exspected essere un intero, anche e nullo non va bene.

4

Il problema è che l'operatore ternario sta inferendo il tipo basato sulla prima assegnazione di parametro ... 10 in questo caso, che è un int, non un nullable int.

Si potrebbe avere più fortuna con:

int? x = GetBoolValue() (int?)10 : null; 
3

È perché il compilatore determina il tipo dell'operatore condizionale è secondo e terzo operando, non per quello di assegnare il risultato. Non esiste una trasmissione diretta tra un numero intero e un riferimento null che il compilatore può utilizzare per determinare il tipo.

12

Per inciso, l'implementazione Microsoft del compilatore C# in realtà ottiene l'analisi del tipo dell'operatore condizionale errata in un modo molto sottile e interessante (per me). Il mio articolo su di esso è Type inference woes, part one.

13

Prova questo:

int? result = condition ? 10 : default(int?);