Remarque: cela semble avoir été corrigé à Roslyn
Cette question s'est posée lors de l'écriture de ma réponse à celle-ci , qui parle de l'associativité de l' opérateur de coalescence nulle .
Pour rappel, l'idée de l'opérateur de coalescence nulle est qu'une expression de la forme
x ?? y
évalue d'abord x
, puis:
- Si la valeur de
x
est nulle,y
est évaluée et c'est le résultat final de l'expression - Si la valeur de
x
n'est pas nulle,y
n'est pas évaluée et la valeur dex
est le résultat final de l'expression, après une conversion au type de compilationy
si nécessaire
Maintenant, généralement, il n'y a pas besoin de conversion, ou c'est juste d'un type nullable à un type non nullable - généralement les types sont les mêmes, ou simplement de (disons) int?
à int
. Cependant, vous pouvez créer vos propres opérateurs de conversion implicites et ceux-ci sont utilisés si nécessaire.
Pour le cas simple de x ?? y
, je n'ai vu aucun comportement étrange. Cependant, avec (x ?? y) ?? z
je vois un comportement déroutant.
Voici un programme de test court mais complet - les résultats sont dans les commentaires:
using System;
public struct A
{
public static implicit operator B(A input)
{
Console.WriteLine("A to B");
return new B();
}
public static implicit operator C(A input)
{
Console.WriteLine("A to C");
return new C();
}
}
public struct B
{
public static implicit operator C(B input)
{
Console.WriteLine("B to C");
return new C();
}
}
public struct C {}
class Test
{
static void Main()
{
A? x = new A();
B? y = new B();
C? z = new C();
C zNotNull = new C();
Console.WriteLine("First case");
// This prints
// A to B
// A to B
// B to C
C? first = (x ?? y) ?? z;
Console.WriteLine("Second case");
// This prints
// A to B
// B to C
var tmp = x ?? y;
C? second = tmp ?? z;
Console.WriteLine("Third case");
// This prints
// A to B
// B to C
C? third = (x ?? y) ?? zNotNull;
}
}
Nous avons donc trois types de valeurs personnalisés A
, B
et C
, avec les conversions de A en B, A en C et B en C.
Je peux comprendre à la fois le deuxième cas et le troisième cas ... mais pourquoi y a- t -il une conversion A vers B supplémentaire dans le premier cas? En particulier, je m'attendais vraiment à ce que le premier et le deuxième cas soient la même chose - il s'agit juste d'extraire une expression dans une variable locale, après tout.
Des preneurs sur ce qui se passe? Je suis extrêmement réticent à crier "bug" en ce qui concerne le compilateur C #, mais je suis perplexe quant à ce qui se passe ...
EDIT: D'accord, voici un exemple plus méchant de ce qui se passe, grâce à la réponse du configurateur, ce qui me donne une raison supplémentaire de penser que c'est un bug. EDIT: L'échantillon n'a même plus besoin de deux opérateurs de coalescence nulle maintenant ...
using System;
public struct A
{
public static implicit operator int(A input)
{
Console.WriteLine("A to int");
return 10;
}
}
class Test
{
static A? Foo()
{
Console.WriteLine("Foo() called");
return new A();
}
static void Main()
{
int? y = 10;
int? result = Foo() ?? y;
}
}
La sortie de ceci est:
Foo() called
Foo() called
A to int
Le fait d' Foo()
être appelé deux fois ici me surprend énormément - je ne vois aucune raison pour que l'expression soit évaluée deux fois.
C? first = ((B?)(((B?)x) ?? ((B?)y))) ?? ((C?)z);
. Vous obtiendrez:Internal Compiler Error: likely culprit is 'CODEGEN'
(("working value" ?? "user default") ?? "system default")