Perché questo codice non è valido in C #?

Il seguente codice non verrà compilato:

string foo = "bar"; Object o = foo == null ? DBNull.Value : foo; 

Ricevo: Errore 1: il tipo di espressione condizionale non può essere determinato perché non esiste una conversione implicita tra “System.DBNull” e “string”

Per risolvere questo problema, devo fare qualcosa del genere:

 string foo = "bar"; Object o = foo == null ? DBNull.Value : (Object)foo; 

Questo cast sembra inutile in quanto è certamente legale:

 string foo = "bar"; Object o = foo == null ? "gork" : foo; 

Mi sembra che quando i rami ternari sono di tipi diversi, il compilatore non autobox i valori per il tipo object … ma quando sono dello stesso tipo, allora l’autoboxing è automatico.

Nella mia mente la prima affermazione dovrebbe essere legale …

Qualcuno può descrivere perché il compilatore non lo consente e perché i progettisti di C # hanno scelto di farlo? Credo che questo sia legale in Java … Anche se non l’ho verificato.

Grazie.

EDIT: Sto chiedendo di capire perché Java e C # gestiscono questo in modo diverso, cosa succede sotto le scene in C # che lo rendono invalido. So usare il ternario e non sto cercando un “modo migliore” per codificare gli esempi. Capisco le regole del ternario in C #, ma voglio sapere PERCHÉ …

EDIT (Jon Skeet): Rimosso il tag “autoboxing” in quanto in questa domanda non sono coinvolti boxe.

Il compilatore richiede che i tipi di secondo e terzo operando siano uguali o che uno sia implicitamente convertibile nell’altro. Nel tuo caso, i tipi sono DBNull e stringa, nessuno dei quali è implicitamente convertibile nell’altro. Lanciare uno di essi all’object risolve quello.

EDIT: Sembra che sia davvero legale in Java. Abbastanza come si risolve cosa fare quando si tratta di sovraccaricare il metodo, non sono sicuro … Ho appena guardato il JLS, ed è estremamente incerto su quale sia il tipo di condizionale quando ci sono due riferimenti incompatibili tipi coinvolti Il modo di lavorare C # può essere più irritante occasionalmente, ma è più chiaro.

La sezione pertinente della specifica C # 3.0 è 7.13, l’operatore condizionale:

Il secondo e il terzo operando dell’operatore?: Controllano il tipo di espressione condizionale. Sia X e Y i tipi del secondo e del terzo operando. Poi,

  • Se X e Y sono dello stesso tipo, allora questo è il tipo di condizionale
  • Altrimenti, se una conversione implicita (§6.1) esiste da X a Y, ma non da Y a X, allora Y è il tipo di espressione condizionale.
  • Altrimenti, se esiste una conversione implicita (§6.1) da Y a X, ma non da X a Y, X è il tipo di espressione condizionale.
  • Altrimenti, non è ansible determinare alcun tipo di espressione e si verifica un errore in fase di compilazione.

DBNull.Value restituisce il tipo DBNull .

Vuoi che il tipo sia una string .

Mentre la string può essere null non può essere un DBNull .

Nel tuo codice l’istruzione a destra degli uguali viene eseguita prima dell’assegnazione all’object.

Fondamentalmente se usi:

 [condition] ? true value : false value; 

In .Net entrambe le opzioni true e false devono essere implicitamente convertibili nello stesso tipo, prima di qualsiasi attribuzione.

Questo è il risultato di come C # riguarda la sicurezza del tipo. Ad esempio il seguente è valido:

 string item = "item"; var test = item != null ? item : "BLANK"; 

C # 3 non supporta i tipi dinamici, quindi cos’è il test? In C # ogni assegnazione è anche un’istruzione con un valore di ritorno, quindi sebbene il costrutto var sia nuovo in C # 3, l’istruzione a destra degli uguali deve sempre essere risolta in un singolo tipo.

In C # 4 e versioni successive puoi supportare esplicitamente i tipi dinamici, ma non credo che questo aiuti qui.

A proposito, il tuo codice è un caso speciale che non deve assolutamente utilizzare l’operatore condizionale. Invece, l’operatore null coalesce è più appropriato (ma richiede comunque il casting):

 object result = (object)foo ?? DBNull.Value;