Pregunta

Falla:

object o = ((1==2) ? 1 : "test");

Tiene éxito:

object o;
if (1 == 2)
{
    o = 1;
}
else
{
    o = "test";
}

El error en la primera declaración es:

El tipo de expresión condicional no se puede determinar porque no hay una conversión implícita entre 'int' y 'string'.

Sin embargo, ¿por qué debe haber que estar asignando esos valores a una variable de objeto de tipo?

Editar: El ejemplo anterior es trivial, sí, pero hay ejemplos en los que esto sería bastante útil:

int? subscriptionID; // comes in as a parameter

EntityParameter p1 = new EntityParameter("SubscriptionID", DbType.Int32)
{
    Value = ((subscriptionID == null) ? DBNull.Value : subscriptionID),
}
¿Fue útil?

Solución

usar:

object o = ((1==2) ? (object)1 : "test");

El problema es que el tipo de retorno del operador condicional no se puede determinar sin ambigüedades. Es decir, entre Int y String, no hay la mejor opción. El compilador siempre usará el tipo de expresión verdadera e implícitamente lanzará la expresión falsa si es necesario.

Editar:En tu segundo ejemplo:

int? subscriptionID; // comes in as a parameter

EntityParameter p1 = new EntityParameter("SubscriptionID", DbType.Int32)
{
    Value = subscriptionID.HasValue ? (object)subscriptionID : DBNull.Value,
}

PD:
Eso no se llama el 'operador ternario'. Eso es Un operador ternario, pero se llama 'operador condicional'.

Otros consejos

Aunque las otras respuestas son correcto, en el sentido de que hacen declaraciones verdaderas y relevantes, hay algunos puntos sutiles de diseño de lenguaje aquí que aún no se han expresado. Muchos factores diferentes contribuyen al diseño actual del operador condicional.

Primero, es deseable para tantas expresiones como sea posible tener un tipo inequívoco que se pueda determinar únicamente a partir del contenido de la expresión. Esto es deseable por varias razones. Por ejemplo: hace que la construcción de un motor Intellisense sea mucho más fácil. Tus escribes x.M(some-expression. y IntelliSense necesita poder analizar alguna expresión, determine su tipo y produzca un menú desplegable antes de que IntelliSense sepa a qué método se refiere XM. Intellisense no puede saber a qué se refiere XM con seguridad si M está sobrecargado hasta que ve todos los argumentos, pero aún no ha escrito ni siquiera el primer argumento.

En segundo lugar, preferimos que la información de tipo fluya "de adentro hacia afuera", debido al escenario que acabo de mencionar: la resolución de sobrecarga. Considera lo siguiente:

void M(object x) {}
void M(int x) {}
void M(string x) {}
...
M(b ? 1 : "hello");

¿Qué debe hacer esto? ¿Debería llamar a la sobrecarga del objeto? ¿Debería a veces llamar a la sobrecarga de cadenas y a veces llamar a la sobrecarga int? ¿Y si tuvieras otra sobrecarga? M(IComparable x) - ¿Cuándo lo eliges?

Las cosas se vuelven muy complicadas cuando la información del tipo "fluye en ambos sentidos". Decir "Estoy asignando esta cosa a una variable de objeto de tipo, por lo tanto, el compilador debe saber que está bien elegir el objeto como el tipo" no se lava; a menudo es el caso que No sabemos el tipo de variable a la que está asignando porque eso es lo que estamos en el proceso de intentar descubrir. La resolución de sobrecarga es exactamente el proceso de resolver los tipos de los parámetros, que son las variables a las que está asignando los argumentos, desde los tipos de argumentos. Si los tipos de los argumentos dependen de los tipos a los que se les asigna, entonces tenemos una circularidad en nuestro razonamiento.

La información de tipo "fluye en ambos sentidos" para las expresiones lambda; Implementar eso de manera eficiente me llevó la mayor parte del año. He escrito una larga serie de artículos que describen algunas de las dificultades para diseñar e implementar un compilador que pueda hacer análisis donde la información de tipo fluye en expresiones complejas basadas en el contexto en el que posiblemente se está utilizando la expresión; La primera parte está aquí:

http://blogs.msdn.com/ericlippert/archive/2007/01/10/lambda-expressions-vs-anonymous-methods-tart-one.aspx

Podrías decir "Bueno, está bien, veo por qué el hecho de que estoy asignando a Object no puede ser utilizado de manera segura por el compilador, y veo por qué es necesario que la expresión tenga un tipo inequívoco, pero ¿por qué no es el tipo de tipo? del objeto de expresión, ya que tanto int y string son convertibles en objeto? " Esto me lleva a mi tercer punto:

En tercer lugar, uno de los principios de diseño sutiles pero consistentemente aplicados de C# es "No produzca tipos por magia". Cuando se les da una lista de expresiones de las cuales debemos determinar un tipo, El tipo que determinamos siempre está en la lista en algún lugar. Nunca nos magamos un nuevo tipo y lo elegimos para ti; El tipo que obtienes siempre es uno que nos diste para elegir. Si dice que encuentre el mejor tipo en un conjunto de tipos, encontramos el mejor tipo en ese conjunto de tipos. En el conjunto {int, String}, no hay el mejor tipo común, la forma en que hay en, digamos, "animal, tortuga, mamífero, wallaby". Esta decisión de diseño se aplica al operador condicional, a los escenarios de unificación de inferencia de tipo, a la inferencia de tipos de matriz tipados implícitamente, etc.

La razón de esta decisión de diseño es que facilita que los humanos comunes resuelvan lo que el compilador hará en cualquier situación en la que se debe determinar un mejor tipo; Si sabe que un tipo que está allí, mirarlo a la cara, será elegido, entonces es mucho más fácil resolver lo que va a suceder.

También evita que tengamos que resolver muchas reglas complejas sobre cuál es el mejor tipo común de un conjunto de tipos cuando hay conflictos. Supongamos que tiene tipos {foo, bar}, donde ambas clases implementan IBLAH, y ambas clases heredan de Baz. ¿Cuál es el mejor tipo común, IBLAH, que tanto implementan, o Baz, que ambos se extienden? No queremos tener que responder a esta pregunta; Queremos evitarlo por completo.

Finalmente, noto que el compilador C# realmente obtiene la determinación de los tipos sutilmente incorrectos en algunos casos oscuros. Mi primer artículo sobre eso está aquí:

http://blogs.msdn.com/ericlippert/archive/2006/05/24/type-inference-woes-tart-one.aspx

Es discutible que, de hecho, el compilador lo haga bien y la especificación es incorrecta; El diseño de implementación es en mi opinión mejor que el diseño específico.

De todos modos, esas son solo algunas razones para el diseño de este aspecto particular del operador ternario. Aquí hay otras sutilezas, por ejemplo, cómo el verificador CLR determina si se garantiza que un conjunto dado de ramas de ramificación deja el tipo correcto en la pila en todas las rutas posibles. Discutir que en detalle me llevaría bastante lejos.

¿Por qué es la función X de esta manera es a menudo una pregunta muy difícil de responder? Es mucho más fácil responder al comportamiento real.

Mi adivina educada sobre por qué. El operador condicional puede usar sucintamente y de manera terselamente una expresión booleana para elegir entre 2 valores relacionados. Deben estar relacionados porque se están utilizando en una sola ubicación. Si el usuario, en cambio, elige 2 valores no relacionados, tal vez tuviera un error tipográfico / error sutil en el código y el compilador es mejor alertarlos sobre esto en lugar de lanzar implícitamente al objeto. Que puede ser algo que no esperaban.

"int" es un tipo primitivo, no un objeto, mientras que "cadena" se considera más un "objeto primitivo". Cuando haces algo como "Objeto O = 1", en realidad estás boxeando el "int" a un "int32". Aquí hay un enlace a un artículo sobre el boxeo:

http://msdn.microsoft.com/en-us/magazine/cc301569.aspx

En general, el boxeo debe evitarse debido a las pérdidas de rendimiento que son difíciles de rastrear.

Cuando usa una expresión ternaria, el compilador no mira la variable de asignación para determinar cuál es el tipo final. Para desglosar su declaración original en lo que está haciendo el compilador:

Declaración: objeto o = ((1 == 2)? 1: "prueba");

Compilador:

  1. ¿Cuáles son los tipos de "1" y "prueba" en '((1 == 2)? 1: "prueba")'? ¿Se complementan?
  2. ¿El tipo final de #1 coincide con el tipo de operador de asignación para 'Object O'?

Dado que el compilador no evalúa el #2 hasta que se realice el n. ° 1, falla.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top