2009-07-23 15 views
40

tengo este extracto de C# 2.0 código fuente:C#: Operador condicional

object valueFromDatabase; 
decimal result; 
valueFromDatabase = DBNull.Value; 

result = (decimal)(valueFromDatabase != DBNull.Value ? valueFromDatabase : 0); 
result = (valueFromDatabase != DBNull.Value ? (decimal)valueFromDatabase : (decimal)0); 

La primera evaluación de los resultados, se emite una InvalidCastException mientras que el segundo no. ¿Cuál es la diferencia entre estos dos?

+0

Quiero aclarar que en las primeras se evalúa a cero y no a DBNull.value. ¿Por qué lanzar 0 a tiros decimales una excepción? – Ioannis

Respuesta

100

ACTUALIZACIÓN: Esta cuestión fue the subject of my blog on May 27th 2010. Gracias por la gran pregunta!

Aquí hay muchas respuestas muy confusas. Déjame intentar responder tu pregunta con precisión. Simplifiquemos esto:

object value = whatever; 
bool condition = something; 
decimal result = (decimal)(condition ? value : 0); 

¿Cómo interpreta el compilador la última línea? El problema que enfrenta el compilador es que el tipo de la expresión condicional debe ser coherente para ambas ramas; las reglas de idioma no le permiten devolver el objeto en una rama e int en la otra. Las opciones son object e int. Cada int es convertible a objeto, pero no todos los objetos son convertibles a int, por lo que el compilador elige el objeto. Por lo tanto, esto es lo mismo que

decimal result = (decimal)(condition ? (object)value : (object)0); 

Por lo tanto, el cero devuelto es una caja int.

A continuación, destraba la int a decimal. Es ilegal desempaquetar un recuadro int a decimal. Por las razones por las cuales, véase mi artículo de blog sobre el tema:

Representation and Identity

Básicamente, el problema es que estás actuando como si el elenco a decimal se distribuyeron, como esto:

decimal result = condition ? (decimal)value : (decimal)0; 

Pero como hemos visto, eso no es lo

decimal result = (decimal)(condition ? value : 0); 

medios. Eso significa "convertir ambas alternativas en objetos y luego desempaquetar el objeto resultante".

+7

+1 Buena explicación. –

+0

No entiendo por qué el compilador está insertando conversiones de tipo donde el error de compilación parece ser más apropiado. – raj

+2

@raj: ¿Qué regla le gustaría ver implementada para determinar cuándo dar un error de compilación? –

12

La diferencia es que el compilador no puede determinar un tipo de datos que coincida bien entre Object y Int32.

Puede convertir explícitamente el valor int a object para obtener el mismo tipo de datos en el segundo y tercer operando de manera que compila, sino la de Couse significa que usted está boxeo y unboxing del valor:

result = (decimal)(valueFromDatabase != DBNull.value ? valueFromDatabase : (object)0); 

Eso se compilará, pero no se ejecutará. Usted tiene a la caja un valor decimal a desempacar como un valor decimal:

result = (decimal)(valueFromDatabase != DBNull.value ? valueFromDatabase : (object)0M); 
+0

C# Especificación: "Una conversión de boxeo permite que un tipo de valor se convierta implícitamente en un tipo de referencia. Existe una conversión de boxeo desde cualquier tipo de valor no anulable a objeto, a System.ValueType y a cualquier tipo de interfaz implementado por el tipo de valor no anulable Además, un tipo de enumeración se puede convertir al tipo System.Enum. " Así que hay una conversión de cuadro implícita entre Int32 y el objeto. Si el compilador no pudo determinar el tipo, fallaría en el momento de la compilación y no en el tiempo de ejecución. Por favor, consulte mi respuesta ... –

3

La x: y parte necesitan un tipo común, el valor de la base de datos es probable que algún tipo de flotador y 0 es un int. Esto sucede antes del lanzamiento a decimal. Pruebe ": 0.0" o ": 0D".

+0

Lo que el valor de la base de datos en realidad es no tiene relevancia para el compilador.El tipo de la variable es Object, y un valor Double no coincidirá mejor que un valor Int32. – Guffa

+1

Como han dicho otros, es 0m – KClough

+0

cero literal del tipo decimal: 0m. – Richard

5

El tipo de operador será un objeto y en el caso de que el resultado deba ser 0, estará enmarcado implícitamente. Pero 0 literal es por defecto tiene tipo int para que cuadro int. Pero con un lanzamiento explícito a decimales, intente desempaquetarlo, lo cual no está permitido (el tipo de recuadro debe ser mucho más grande con el que usted devuelve). Es por eso que puedes obtener una excepción.

Aquí es un extracto de C# Especificación:

El segundo y tercer operandos de la:? Control del operador el tipo de la expresión condicional. Deje que X e Y sean los tipos del segundo y tercer operandos. Luego,

  • Si X e Y son del mismo tipo, este es el tipo de expresión condicional.
  • De lo contrario, si existe una conversión implícita (§6.1) de X a Y, pero no de Y a X, entonces Y es el tipo de la expresión condicional .
  • De lo contrario, si existe una conversión implícita (§6.1) de Y a X, pero no de X a Y, entonces X es el tipo de la expresión condicional .
  • De lo contrario, no se puede determinar ningún tipo de expresión y se produce un error en tiempo de compilación.
2

Si no me equivoco (que es muy posible) es en realidad el 0 que está causando la excepción, y esto se debe a .NET (loco) suponiendo que el tipo de un literal por lo que necesita para especificar 0m en lugar de solo 0.

Consulte MSDN para obtener más información.

0

Hay dos tipos diferentes para que el compilador decida (en tiempo de compilación) cuál lanzar a decimal. Esto no puede hacer.

4

Su línea debe ser:

result = valueFromDatabase != DBNull.value ? (decimal)valueFromDatabase : 0m; 

0m es la constante decimal para el cero

Ambas partes de un operador condicional deben evaluar con el mismo tipo de datos

+0

Probablemente se compile exactamente en el mismo código que la segunda línea. – Guffa

+0

tal vez, pero vale la pena señalar que la constante decimal para cero es –

-1

Su respuesta funcionaría si usted combina ambos:

result = (decimal)(valueFromDatabase != DBNull.Value ? (decimal)valueFromDatabase : (decimal)0); 

Por lo menos, una situación similar colada en un parámetro para mí.

+0

Esto no tiene sentido: desde que lanzas las dos ramas, el lanzamiento inicial es inútil. –

Cuestiones relacionadas