2009-10-09 18 views
36

Recientemente encontré que podemos usar ?? operador para verificar nulos. Por favor, compruebe los siguientes ejemplos de código:¿por qué preferimos? a ?? operador en C#?

var res = data ?? new data(); 

Esto es exactamente similar a

var res = (data==null) ? new data() : data ; 

Revisé toda mi repositorio de código fuente del proyecto y algunos de otros proyectos de código abierto. Y este operador ?? nunca se ha utilizado.

Me pregunto si hay alguna razón detrás de esto, como problemas de rendimiento o algo así?

EDIT:

Acabo de actualizar mi código de ejemplo basado en los comentarios de recursiva & Anton. Es un error en descuidado. :(

+21

Una de las razones es que ppl podría no ser consciente. – vpram86

+1

debería ser "var res = (data! = Null)? Data: new data();" en su muestra –

+2

@Rubens, simplemente tonto y no está bien. – kenny

Respuesta

53

La nula operador coalesce es mucho más claro en la comprobación de null, que es su propósito principal. También puede ser encadenado.

object a = null; 
object b = null; 
object c = new object(); 
object d = a ?? b ?? c; //d == c. 

Mientras que el operador se limita a null comprobación, el operador ternario es no. Por ejemplo

bool isQuestion = true; 
string question = isQuestion ? "Yes" : "No"; 

creo que la gente simplemente no son conscientes de que el operador nulo se unen así que utilizan el operador ternario en su lugar. ternario existía antes de C# en la mayoría de lenguajes de estilo C por lo que si usted no sabe C# en el interior y fuera y/o programado en otro idioma, te rnary es una elección natural. Sin embargo, si está buscando un valor nulo, use el operador nulo coalesce, está diseñado para eso, y el IL está ligeramente optimizado (compare con un if si no con otro).

Este es un ejemplo que compara el uso de cada

object a = null; 
object b = null; 
object c = null; 

object nullCoalesce = a ?? b ?? c; 

object ternary = a != null ? a : b != null ? b : c; 

object ifThenElse; 

if (a != null) 
    ifThenElse = a; 
else if (b != null) 
    ifThenElse = b; 
else if (c != null) 
    ifThenElse = c; 

En primer lugar, basta con ver la sintaxis de coalescencia nulo, que es la forma más clara. Ternary es realmente confuso. Ahora veamos la IL

nulo Coalesce Sólo

.entrypoint 
.maxstack 2 
.locals init (
    [0] object a, 
    [1] object b, 
    [2] object c, 
    [3] object nullCoalesce) 
L_0000: ldnull 
L_0001: stloc.0 
L_0002: ldnull 
L_0003: stloc.1 
L_0004: newobj instance void [mscorlib]System.Object::.ctor() 
L_0009: stloc.2 
L_000a: ldloc.0 
L_000b: dup 
L_000c: brtrue.s L_0015 
L_000e: pop 
L_000f: ldloc.1 
L_0010: dup 
L_0011: brtrue.s L_0015 
L_0013: pop 
L_0014: ldloc.2 
L_0015: stloc.3 
L_0016: ldloc.3 
L_0017: call void [mscorlib]System.Console::WriteLine(object) 
L_001c: ret 

ternario Sólo

.entrypoint 
.maxstack 2 
.locals init (
    [0] object a, 
    [1] object b, 
    [2] object c, 
    [3] object ternary) 
L_0000: ldnull 
L_0001: stloc.0 
L_0002: ldnull 
L_0003: stloc.1 
L_0004: newobj instance void [mscorlib]System.Object::.ctor() 
L_0009: stloc.2 
L_000a: ldloc.0 
L_000b: brtrue.s L_0016 
L_000d: ldloc.1 
L_000e: brtrue.s L_0013 
L_0010: ldloc.2 
L_0011: br.s L_0017 
L_0013: ldloc.1 
L_0014: br.s L_0017 
L_0016: ldloc.0 
L_0017: stloc.3 
L_0018: ldloc.3 
L_0019: call void [mscorlib]System.Console::WriteLine(object) 
L_001e: ret 

si entonces si no Sólo

.entrypoint 
.maxstack 1 
.locals init (
    [0] object a, 
    [1] object b, 
    [2] object c, 
    [3] object ifThenElse) 
L_0000: ldnull 
L_0001: stloc.0 
L_0002: ldnull 
L_0003: stloc.1 
L_0004: newobj instance void [mscorlib]System.Object::.ctor() 
L_0009: stloc.2 
L_000a: ldloc.0 
L_000b: brfalse.s L_0011 
L_000d: ldloc.0 
L_000e: stloc.3 
L_000f: br.s L_001a 
L_0011: ldloc.1 
L_0012: brfalse.s L_0018 
L_0014: ldloc.1 
L_0015: stloc.3 
L_0016: br.s L_001a 
L_0018: ldloc.2 
L_0019: stloc.3 
L_001a: ldloc.3 
L_001b: call void [mscorlib]System.Console::WriteLine(object) 
L_0020: ret 

IL no es uno de mis puntos fuertes, así que quizás alguien pueda editar mi respuesta y expandirla. Iba a explicar mi teoría, pero preferiría no confundirme a mí mismo y a los demás. El número de LOC es similar para los tres, pero no todos los operadores de IL tardan el mismo tiempo en ejecutarse.

+7

No es * el * operador ternario, es solo * un * operador ternario. A lo que se refiere se lo llama ** operador condicional **. –

+1

@Bob, es una buena comparación ... el operador de coalescencia es mejor que todos cuando tratamos con nulos ... – RameshVel

+3

@divo: bueno, es el único operador ternario en C#: P – Lucas

6

Una razón por la que puedo pensar es que este operador se introdujo en .NET 2.0 por lo que el código para .NET 1.1 no puede tenerlo.

Estoy de acuerdo con usted, deberíamos usar esto con más frecuencia.

ref link

12

El ??operador (también conocido como null-coalescing operator) es menos conocido que el operador ternario, ya que hizo su debut con .NET 2.0 y tipos Nullable. Las razones para no usarlo probablemente incluyen no comenzar a darse cuenta de que existe, o estar más familiarizado con el operador ternario.

Dicho esto, la comprobación de nulo no es lo único que el operador ternario es bueno para, así que no es un sustituto del mismo, como tal, más como una mejor alternativa para una necesidad muy específica. :)

1

Creo que es solo un hábito de otros idiomas. HASTA DONDE SE, ?? el operador no se usa en ningún otro idioma.

+2

Sé que esto es muy viejo, pero quería comentar y decir que también se usa en Perl a partir de v5.10, solo con '//' en lugar de '??'. Un ejemplo (tomado de [wikipedia] (http://en.wikipedia.org/wiki/Null_coalescing_operator#Perl)): '$ possible_null_value // $ value_if_null' –

0

yo habría pensado que el equivalente de

var res = data ?? data.toString(); 

sería

var res = (data!=null) ? data : data.toString(); 
2

Una de las razones (como ya se ha tocado) es probable que sea la falta de conciencia. También podría ser (como en mi caso), un deseo de mantener el número de enfoques para hacer cosas similares en una base de código tanto como sea posible. Por lo tanto, tiendo a usar el operador ternario para todas las situaciones compactas de si una condición se cumple.

Por ejemplo, creo que las dos afirmaciones siguientes bastante similar a nivel conceptual:

return a == null ? string.Empty : a;  
return a > 0 ? a : 0; 
4

Basado en Bob's respuesta

public object nullCoalesce(object a, object b, object c) 
{ 
    return a ?? b ?? c; 
} 
public object ternary(object a, object b, object c) 
{ 
    return a != null ? a : b != null ? b : c; 
} 
public object ifThenElse(object a, object b, object c) 
{ 
    if (a != null) 
     return a; 
    else if (b != null) 
     return b; 
    else 
     return c; 
} 

... esta es la IL partir de las versiones de lanzamiento ..

.method public hidebysig instance object nullCoalesce(
    object a, 
    object b, 
    object c) cil managed 
{ 
    .maxstack 8 
    L_0000: ldarg.1 
    L_0001: dup 
    L_0002: brtrue.s L_000b 
    L_0004: pop 
    L_0005: ldarg.2 
    L_0006: dup 
    L_0007: brtrue.s L_000b 
    L_0009: pop 
    L_000a: ldarg.3 
    L_000b: ret 
} 

.method public hidebysig instance object ternary(
    object a, 
    object b, 
    object c) cil managed 
{ 
    .maxstack 8 
    L_0000: ldarg.1 
    L_0001: brtrue.s L_000a 
    L_0003: ldarg.2 
    L_0004: brtrue.s L_0008 
    L_0006: ldarg.3 
    L_0007: ret 
    L_0008: ldarg.2 
    L_0009: ret 
    L_000a: ldarg.1 
    L_000b: ret 
} 

.method public hidebysig instance object ifThenElse(
    object a, 
    object b, 
    object c) cil managed 
{ 
    .maxstack 8 
    L_0000: ldarg.1 
    L_0001: brfalse.s L_0005 
    L_0003: ldarg.1 
    L_0004: ret 
    L_0005: ldarg.2 
    L_0006: brfalse.s L_000a 
    L_0008: ldarg.2 
    L_0009: ret 
    L_000a: ldarg.3 
    L_000b: ret 
} 
Cuestiones relacionadas