2009-05-13 12 views
204

me encontré con un error extraño:Tipos anulables y el operador ternario: ¿por qué es `? 10: nulo` prohibido?

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

Luego, en otro método, algo como esto:

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

simple, si el método devuelve true, asigne 10 a los anulables int x. De lo contrario, asigne nulo al nullable int. Sin embargo, el compilador se queja:

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

¿Estoy volviendo loco?

+2

Tal vez esto se corregirá en una futura versión del compilador, ya que, en realidad hay una conversión implícita entre 'int' y '' y que es 'int?' –

+0

@bruno, ¿Cómo es eso diferente a decir "realmente hay una conversión implícita entre [cualquier tipo] y [cualquier otro tipo] y eso es 'objeto'"? ¿Crees que también "corregirán" eso en una versión futura del compilador? Por ejemplo: object x = GetBoolValue()? (objeto) "foo": (objeto) DateTime.Now; – LukeH

+2

Imagine que el compilador fue lo suficientemente inteligente como para decir "OK, no estamos usando el primer tipo (int), entonces buscaremos otro tipo que podamos usar para el resultado de esta expresión". ¿Qué obtendrías si escribieras "Console.WriteLine ((Predicate()? 5.6: null) .GetType(). ToString());"? ¿Podrías flotar? o doble? ¿u objeto? ¿O una clase definida por el usuario con conversión implícita de float? ¿Cómo sabría el compilador qué tipo elegir, ya que no puede elegir el que está en la expresión? – mquander

Respuesta

336

El compilador primero trata de evaluar la expresión de la derecha:

GetBoolValue() ? 10 : null 

El 10 es un int literal (no int?) y null se , bueno, null. No hay conversión implícita entre esos dos, por lo tanto, el mensaje de error.

Si cambia la expresión de la derecha a una de las siguientes entonces se compila porque hay una conversión implícita entre int? y null (# 1) y entre int y int? (# 2, # 3).

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

También puede escribir 'new int?()'. – SLaks

+36

O incluso mejor en mi humilde opinión: 'default (int?)' – Lucero

+1

Sí, el compilador requiere esta clave antes de ** ajustar ** el 'int' 10 a un' int? '. Tenga en cuenta que otra posibilidad sería ** box ** the 'int' 10 a una clase base o interfaz de' System.Int32'. Como un ejemplo 'GetBoolValue()? (IFormattable) 10: nulo // # 1B' o 'GetBoolValue()? 10: (IFormattable) null // # 2B' estará bien. Esta posibilidad puede ser una razón por la cual no hacen que la envoltura nulable sea automática. Debido a que tanto el embalaje como el boxeo son normalmente _implicit_ conversiones. Eso es tanto 'int? variable = 10; 'y' variable IFormattable = 10; 'son legales (las primeras vueltas, las últimas cajas). –

30

Prueba esto:

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

Básicamente lo que está sucediendo es que el operador condicional es incapaz de determinar el "tipo de retorno" de la expresión. Desde el compilador implictitly decide que es un 10int entonces decide que el tipo de retorno de esta expresión será un int también. Desde un int no puede ser null (el tercer operando del operador condicional) se queja.

Echando la null a un Nullable<int> le estamos diciendo al compilador explícitamente que el tipo de retorno de esta expresión será un Nullable<int>. Usted podría tener la misma facilidad fundido el 10-int? así y tenía el mismo efecto.

+0

Sé que puedo evitarlo, solo tengo curiosidad de por qué está sucediendo esto? – BFree

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

La razón por la que ves esto es porque detrás de las escenas que está utilizando anulable y hay que decir que su C# "nulo" es una instancia nula de anulable.

+0

Doh! Andrew obtuvo el primero. +1 a él. –

4

probar uno de estos:

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

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

podemos agregar otro: int? x = GetBoolValue()? 10: nuevo int?(); – Eniola

3

Sólo añadir un reparto explict.

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

Es el operador ternario que se confunde - El segundo argumento es un entero y también lo es el tercer argumento exspected a ser un número entero, también, y no cabe nula.

4

El problema es que el operador ternario está infiriendo tipo basado en su primera parametrización 10 ... en este caso, que es un int, no un int anulable.

Es posible que tenga mejor suerte con:

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

Es porque el compilador determina el tipo de operador condicional por su segundo y tercer operando, no por lo que asigna el resultado. No hay conversión directa entre un entero y una referencia nula que el compilador puede usar para determinar el tipo.

12

Incidentalmente, la implementación de Microsoft del compilador de C# realmente hace que el análisis de tipo del operador condicional sea incorrecto de una manera muy sutil e interesante (para mí). Mi artículo sobre él es Type inference woes, part one.

13

Prueba esto:

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

Cuestiones relacionadas