usar usa unario solo que poner para operador comparaciones como and c# c#-6.0 null-conditional-operator

unario - para que se usa el en c#



¿Por qué tengo que colocar() alrededor de la expresión condicional nula para usar la sobrecarga del método correcto? (4)

El significado de xY?.Color.IsOneOf(Color.Red, Color.Green) es bastante independiente de las sobrecargas de su método. ?. Los pseudooperandos de s son xY y Color.IsOneOf(Color.Red, Color.Green) , aunque este último por sí solo no es una expresión válida. Primero, se evalúa xY . Llama al resultado tmp . Si tmp == null , toda la expresión es null . Si tmp != null tmp.Color.IsOneOf(Color.Red, Color.Green) la expresión completa se evalúa como tmp.Color.IsOneOf(Color.Red, Color.Green) .

Cuando IsOneOf es un método de instancia, ese es casi siempre el comportamiento que desearía, por lo que es ese comportamiento el que terminó ingresando en la especificación y en el compilador.

Cuando IsOneOf es un método de extensión, como lo es en su ejemplo, puede que no sea el comportamiento que desearía, pero el comportamiento es el mismo, por coherencia y ya que hay una solución disponible que ya ha encontrado.

La asociación y el motivo por el que se eligió el resultado actual se explican en un hilo en el sitio de Roslyn CodePlex.

Tengo estos métodos de extensión y tipo de enumeración:

public static bool IsOneOf<T>(this T thing, params T[] things) { return things.Contains(thing); } public static bool IsOneOf<T>(this T? thing, params T[] things) where T : struct { return thing.HasValue && things.Contains(thing.Value); } public enum Color { Red, Green, Blue }

El primero if abajo compila; el segundo no lo hace

if ((x.Y?.Color).IsOneOf(Color.Red, Color.Green)) ; if (x.Y?.Color.IsOneOf(Color.Red, Color.Green)) ;

Solo varían por el par de paréntesis extra. ¿Por qué tengo que hacer esto?

Al principio sospeché que estaba haciendo un doble lanzamiento implícito de bool? a bool y luego a bool? , pero cuando elimino el primer método de extensión, ¿se queja de que no hay un lanzamiento implícito de bool a bool? . Luego revisé el IL y no hubo moldes. Descompilar de nuevo a C # produce algo que se parece a:

if (!(y != null ? new Color?(y.Color) : new Color?()).IsOneOf<Color>(new Color[2] { Color.Red, Color.Green }));

lo cual está bien para la versión de CLR que estoy ejecutando, y lo que espero. Lo que no esperaba es que xY?.Color.IsOneOf(Color.Red, Color.Green) no compila.

Que esta pasando? ¿Es simplemente la forma en que se implementó el lenguaje que requiere el () ?

Actualizar

Aquí hay una tapa de pantalla que muestra el error en contexto. Esto se está volviendo aún más confuso para mí. El error realmente tiene sentido; pero lo que no (en mi opinión ahora) es por qué la línea 18 no tendría el mismo problema.


En primer lugar, este comportamiento me parece intencional. Es raro que alguien agregue métodos de extensión a tipos anulables, y es muy común que las personas combinen accesos de miembros regulares y nulos en una expresión, por lo que el lenguaje está favoreciendo esta última.

Considere los siguientes ejemplos:

class B { bool c; } class A { B b; } ... A a; var output = a?.b.c; // infers bool?, throws NPE if (a != null && a.b == null) // roughly translates to // var output = (a == null) ? null : a.b.c;

mientras

A a; var output = (a?.b).c; // infers bool, throws NPE if (a == null || a.b == null) // roughly translates to // var output = ((a == null) ? null : a.b).c;

y luego está

A a; var output = a?.b?.c; // infers bool?, *cannot* throw NPE // roughly translates to // var output = (a == null) ? null : (a.b == null) ? null : a.b.c; // and this is almost the same as // var output = (a?.b)?.c; // infers bool?, cannot throw NPE // Only that the second `?.` is forced to evaluate every time.

El objetivo de diseño aquí parece ser ayudar a la distinción entre a?.bc y a?.b?.c a?.bc . Si a es nulo, esperamos obtener una NPE en ninguno de los casos. ¿Por qué? Porque hay un nulo condicional directamente después de a . Por lo tanto, la parte .c solo debe evaluarse si no era nula, lo que hace que el miembro sea dependiente del resultado condicional nulo anterior. Al agregar corchetes explícitos, (a?.b).c forzamos al compilador a que intente acceder a .c of (a?.b) independientemente de que sea nulo, evitando que "cortocircuite" la expresión completa a null. (usando las palabras @JamesBuck -s)

En su caso, xY?.Color.IsOneOf(Color.Red, Color.Green) es como a?.bc . bool IsOneOf(Color red) la función con la firma bool IsOneOf(Color red) (de modo que la sobrecarga donde el parámetro no es anulable, y xY la parte genérica) solo cuando xY no era nula, por lo que se ajusta el tipo de expresión en Nullable para manejar el caso cuando xY es nulo ¿Y porque a la vez se evalúa a bool? en lugar de bool , no se puede utilizar como prueba en una sentencia if.


Ok, lo descubrí, pero voy con la answer Tamas. Sin embargo, creo que el mío tiene valor aquí como ayuda para el pensamiento cuando se trabaja con condiciones nulas.

La respuesta fue como mirarme en la captura de pantalla en mi pregunta.

¿Estaba viendo el túnel en el Color / Color? conversión, pero el problema real es que bool? no se puede lanzar implícitamente a bool .

En una larga expresión "punteada" que contiene el ?. operador, hay que pensar en términos de la cosa, y su tipo, después del punto más a la derecha . Esa cosa, en este caso, es el método de extensión que devuelve un bool , pero el uso de ?. efectivamente "lo cambia" a un bool? . En otras palabras, piense en lo más correcto como un tipo de referencia que podría ser null o un Nullable<T> .

Wow, este me tenía yendo.


Si coloca la siguiente línea, verá que .net está creando un tipo dinámico System.Nullable<UserQuery+Color> para esta expresión condicional nula dentro de los paréntesis.

Console.WriteLine((xY?.Color).GetType().ToString());

Si no pone los paréntesis, probablemente trate de evaluar como una expresión normal, por lo tanto, el error.