J'ai récemment trouvé que nous pouvons utiliser ?? opérateur pour vérifier les valeurs nulles. Veuillez vérifier les exemples de code ci-dessous:
var res = data ?? new data();
C'est exactement similaire à
var res = (data==null) ? new data() : data ;
J'ai vérifié l'ensemble du référentiel source de mon projet et certains autres projets open source. Et ça ??
opérateur n'a jamais été utilisé.
Je me demande simplement s'il y a une raison derrière cela, comme des problèmes de performances ou quelque chose du genre?
MODIFIER:
Je viens de mettre à jour mon exemple de code en fonction des commentaires de recursive & Anton. C'est une erreur de négligence. :(
L'opérateur de fusion null est beaucoup plus clair lors de la vérification de null, c'est son objectif principal. Il peut également être enchaîné.
object a = null;
object b = null;
object c = new object();
object d = a ?? b ?? c; //d == c.
Alors que cet opérateur est limité à la vérification de null, l'opérateur ternaire ne l'est pas. Par exemple
bool isQuestion = true;
string question = isQuestion ? "Yes" : "No";
Je pense que les gens ne sont tout simplement pas conscients de l'opérateur de fusion nul, ils utilisent donc l'opérateur ternaire à la place. Le ternaire existait avant C # dans la plupart des langages de style C donc si vous ne connaissez pas C # à l'intérieur et à l'extérieur et/ou que vous avez programmé dans un autre langage, le ternaire est un choix naturel. Si vous vérifiez la valeur null, utilisez l'opérateur de fusion null, il est conçu pour cela et l'IL est légèrement optimisé (comparez ?? à un if then else).
Voici un exemple comparant l'utilisation de chacun
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;
Tout d'abord, regardez simplement la syntaxe de la fusion nulle, c'est beaucoup plus clair. Ternary est vraiment déroutant. Regardons maintenant l'IL
Null Coalesce uniquement
.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
Ternaire uniquement
.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 alors sinon seulement
.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 n'est pas l'un de mes points forts, alors peut-être que quelqu'un peut modifier ma réponse et la développer. J'allais expliquer ma théorie, mais je préfère ne pas me confondre avec les autres. Le nombre de LOC est similaire pour les trois, mais tous les opérateurs IL ne prennent pas le même temps pour s'exécuter.
Le ?? (également connu sous le nom de opérateur de fusion null ) est moins connu que l'opérateur ternaire, car il a fait ses débuts avec .NET 2.0 et les types Nullable. Les raisons de ne pas l'utiliser incluent probablement ne pas commencer à savoir qu'il existe, ou être plus familier avec l'opérateur ternaire.
Cela dit, la vérification de null n'est pas la seule chose pour laquelle l'opérateur ternaire est bon, donc ce n'est pas un remplacement pour lui en tant que tel, mais plutôt une meilleure alternative pour un besoin très spécifique. :)
Une raison à laquelle je peux penser est que cet opérateur a été introduit dans .NET 2.0, de sorte que le code pour .NET 1.1 ne peut pas l'avoir.
Je suis d'accord avec vous, nous devrions l'utiliser plus souvent.
réf lien
Basé sur Bob's answer
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;
}
... c'est l'IL des versions de version ...
.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
}
Une des raisons (comme d'autres l'ont déjà touché) est probablement le manque de sensibilisation. Cela pourrait aussi être (comme dans mon cas), un souhait de réduire autant que possible le nombre d'approches pour faire des choses similaires dans une base de code. J'ai donc tendance à utiliser l'opérateur ternaire pour toutes les situations compactes si-une-condition-est-remplie-faire-ceci-autrement-faire-cela.
Par exemple, je trouve les deux déclarations suivantes assez similaires sur le plan conceptuel:
return a == null ? string.Empty : a;
return a > 0 ? a : 0;
Je pense que c'est juste une habitude d'autres langues. AUTANT QUE JE SACHE, ?? L'opérateur n'est utilisé dans aucune autre langue.
J'aurais pensé l'équivalent de
var res = data ?? data.toString();
serait
var res = (data!=null) ? data : data.toString();