113 votes

Pourquoi y a-t-il une différence dans la vérification de la valeur nulle par rapport à une valeur en VB.NET et C#?

Dans VB.NET cela se produit :

Dim x As System.Nullable(Of Decimal) = Nothing
Dim y As System.Nullable(Of Decimal) = Nothing

y = 5
If x <> y Then
    Console.WriteLine("true")
Else
    Console.WriteLine("false") '' <-- Je comprends pourquoi.
End If

Mais en C# cela se produit :

decimal? x = default(decimal?);
decimal? y = default(decimal?);

y = 5;
if (x != y)
{
    Debug.WriteLine("true"); // <-- Je comprends -- Je suis d'accord, C# :)
}
else
{
    Debug.WriteLine("false");
}

Pourquoi y a-t-il une différence ?

23 votes

C'est terrifiant.

8 votes

Je crois que default(decimal?) renvoie 0, non null.

7 votes

@RyanFrame NON. Puisqu'il s'agit de types nullable, cela renvoie null

90voto

Pieter Geerkens Points 5506

VB.NET et C#.NET sont des langages différents, construits par des équipes différentes qui ont fait des hypothèses différentes sur l'utilisation; dans ce cas, la sémantique de la comparaison à NULL.

Ma préférence personnelle va pour la sémantique de VB.NET, qui donne essentiellement à NULL la signification "Je ne sais pas encore". Donc la comparaison de 5 à "Je ne sais pas encore". est naturellement "Je ne sais pas encore"; c'est-à-dire NULL. Cela présente l'avantage supplémentaire de refléter le comportement de NULL dans (la plupart, si ce n'est pas tous) les bases de données SQL. Il s'agit également d'une interprétation plus standard (que celle de C#) de la logique à trois valeurs, comme expliqué ici.

L'équipe de C# a fait des hypothèses différentes sur la signification de NULL, ce qui entraîne la différence de comportement que vous montrez. Eric Lippert a écrit un blog sur la signification de NULL en C#. Selon Eric Lippert: "J'ai également écrit sur la sémantique des nulls en VB / VBScript et JScript ici et ici".

Dans tout environnement où les valeurs NULL sont possibles, il est important de reconnaître que la Loi du Tiers Exclu (c'est-à-dire que A ou ~A est tautologiquement vrai) ne peut plus être fiable.

Mise à jour:

Un bool (par opposition à un bool?) ne peut prendre que les valeurs TRUE et FALSE. Cependant, une implémentation en langage de programmation de NULL doit décider comment NULL se propage à travers les expressions. En VB, les expressions 5=null et 5<>null RETOURNENT TOUS deux false. En C#, des expressions comparables 5==null et 5!=null SEULEMENT le second premier [mis à jour le 2014-03-02 - PG] retourne false. Cependant, dans n'importe quel environnement qui supporte NULL, il incombe au programmeur de connaître les tables de vérité et la propagation de NULL utilisées par ce langage.

Mise à jour

Les articles de blog d'Eric Lippert (mentionnés dans ses commentaires ci-dessous) sur la sémantique sont désormais disponibles sur:

4 votes

Merci pour le lien. J'ai aussi écrit sur la sémantique des valeurs nulles en VB / VBScript et JScript ici : blogs.msdn.com/b/ericlippert/archive/2003/09/30/53120.aspx et ici : blogs.msdn.com/b/ericlippert/archive/2003/10/01/53128.aspx

27 votes

Et FYI, la décision de rendre C# incompatible avec VB de cette manière a été controversée. Je ne faisais pas partie de l'équipe de conception du langage à l'époque, mais le nombre de débats ayant entouré cette décision était considérable.

0 votes

@EricLippert : Merci; je vais jeter un œil à ces liens supplémentaires.

37voto

Tim Schmelter Points 163781

Parce que x <> y renvoie Nothing au lieu de true. Il n'est tout simplement pas défini car x n'est pas défini. (similaire à SQL null).

Remarque: VB.NET Nothing <> C# null.

Vous devez également comparer la valeur d'un Nullable(Of Decimal) uniquement s'il a une valeur.

Donc le VB.NET ci-dessus se compare à ceci (qui semble moins incorrect):

If x.HasValue AndAlso y.HasValue AndAlso x <> y Then
    Console.WriteLine("true")
Else
    Console.WriteLine("false")  
End If

La spécification du langage VB.NET:

7.1.1 Types de Valeurs Nullable ... Un type de valeur nullable peut contenir les mêmes valeurs que la version non nullable du type ainsi que la valeur null. Ainsi, pour un type de valeur nullable, assigner Nothing à une variable de type définit la valeur de la variable à la valeur null, et non à la valeur zéro du type de valeur.

Par exemple:

Dim x As Integer = Nothing
Dim y As Integer? = Nothing

Console.WriteLine(x) ' Affiche zéro '
Console.WriteLine(y) ' N'affiche rien (car la valeur de y est la valeur null) '

16 votes

"VB.NET Nothing <> C# null" cela renvoie vrai pour C# et faux pour VB.Net? Juste pour rire :-p

17voto

Yossarian Points 8601

Regardez le CIL généré (j'ai converti les deux en C#) :

C# :

private static void Main(string[] args)
{
    decimal? x = null;
    decimal? y = null;
    y = 5M;
    decimal? CS$0$0000 = x;
    decimal? CS$0$0001 = y;
    if ((CS$0$0000.GetValueOrDefault() != CS$0$0001.GetValueOrDefault()) ||
        (CS$0$0000.HasValue != CS$0$0001.HasValue))
    {
        Console.WriteLine("true");
    }
    else
    {
        Console.WriteLine("false");
    }
}

Visual Basic :

[STAThread]
public static void Main()
{
    decimal? x = null;
    decimal? y = null;
    y = 5M;
    bool? VB$LW$t_struct$S3 = new bool?(decimal.Compare(x.GetValueOrDefault(), y.GetValueOrDefault()) != 0);
    bool? VB$LW$t_struct$S1 = (x.HasValue & y.HasValue) ? VB$LW$t_struct$S3 : null;
    if (VB$LW$t_struct$S1.GetValueOrDefault())
    {
        Console.WriteLine("true");
    }
    else
    {
        Console.WriteLine("false");
    }
}

Vous verrez que la comparaison en Visual Basic retourne Nullable (pas bool, false ou true !). Et undefined converti en bool est false.

Nothing comparé à quoi que ce soit est toujours Nothing, pas false en Visual Basic (c'est la même chose qu'en SQL).

0 votes

Pourquoi répondre à la question par essais et erreurs ? Il devrait être possible de le faire à partir des spécifications du langage.

3 votes

@DavidHeffernan, car cela montre une différence de langage assez claire.

2 votes

@Yossarian Vous pensez que les spécifications du langage sont ambiguës sur la question. Je ne suis pas d'accord. L'IL est un détail de mise en œuvre sujet à modification ; les spécifications ne le sont pas.

6voto

supercat Points 25534

Le problème observé ici est un cas particulier d'un problème plus général, à savoir que le nombre de définitions différentes de l'égalité qui peuvent être utiles dans au moins certaines circonstances dépasse le nombre de moyens couramment disponibles pour les exprimer. Ce problème est dans certains cas aggravé par une croyance malheureuse selon laquelle il est déroutant d'avoir différents moyens de tester l'égalité donnant des résultats différents, et une telle confusion pourrait être évitée en faisant en sorte que les différentes formes d'égalité donnent les mêmes résultats chaque fois que possible.

En réalité, la cause fondamentale de la confusion est une croyance erronée selon laquelle les différentes formes de test d'égalité et d'inégalité devraient être censées donner le même résultat, malgré le fait que différentes sémantiques sont utiles dans des circonstances différentes. Par exemple, d'un point de vue arithmétique, il est utile de pouvoir avoir des Decimal qui diffèrent seulement par le nombre de zéros en fin de comparaison comme égaux. De même pour des valeurs de double comme zéro positif et zéro négatif. En revanche, d'un point de vue de mise en cache ou d'internement, de telles sémantiques peuvent être mortelles. Supposons, par exemple, que l'on ait un Dictionary tel que myDict[someDecimal] devrait être égal à someDecimal.ToString(). Un tel objet semblerait raisonnable si l'on avait de nombreuses valeurs de Decimal que l'on voulait convertir en chaîne et que l'on attendait qu'il y ait de nombreuses duplications. Malheureusement, si l'on utilisait une telle mise en cache pour convertir 12.3 m et 12.40 m, suivis de 12.30 m et 12.4 m, ces dernières valeurs donneraient "12.3" et "12.40" au lieu de "12.30" et "12.4".

En revenant à la question en cours, il existe plus d'une façon raisonnable de comparer des objets nuls pour l'égalité. C# adopte le point de vue que son opérateur == doit refléter le comportement de Equals. VB.NET adopte le point de vue que son comportement devrait refléter celui de certains autres langages, puisque quiconque veut le comportement de Equals pourrait utiliser Equals. En un sens, la bonne solution serait d'avoir une construction "if" à trois voies, et exiger que si l'expression conditionnelle retourne un résultat à trois valeurs, le code doit spécifier ce qui devrait se produire dans le cas de null. Comme cela n'est pas une option avec les langues telles qu'elles sont, la meilleure alternative est simplement d'apprendre comment fonctionnent différentes langues et de reconnaître qu'elles ne sont pas identiques.

Accessoirement, l'opérateur "Is" de Visual Basic, qui fait défaut en C, peut être utilisé pour tester si un objet nul est effectivement nul. Bien que l'on puisse raisonnablement se demander si un test if devrait accepter un Boolean?, le fait que les opérateurs de comparaison normaux retournent Boolean? plutôt que Boolean lorsqu'ils sont invoqués sur des types nullables est une fonctionnalité utile. Par ailleurs, en VB.NET, si l'on tente d'utiliser l'opérateur d'égalité au lieu de Is, on obtiendra un avertissement indiquant que le résultat de la comparaison sera toujours Nothing, et on devrait utiliser Is si l'on veut tester si quelque chose est nul.

0 votes

Tester si une classe est nulle en C# se fait avec == null. Et tester si un type de valeur nullable possède une valeur se fait avec .hasValue. Quel est l'intérêt d'un opérateur Is Nothing? C# dispose bien de is mais il teste la compatibilité des types. Au vu de tout cela, je ne suis vraiment pas sûr de ce que votre dernier paragraphe essaie de dire.

0 votes

@ErikE: Les types nullables en vb.net et C# peuvent être vérifiés pour une valeur en comparant à null, bien que les deux langages considèrent cela comme du sucre syntaxique pour une vérification de HasValue, du moins dans les cas où le type est connu (je ne suis pas sûr du code généré pour les génériques).

0 votes

Dans les génériques, vous pouvez rencontrer des problèmes complexes liés aux types nullable et à la résolution de surcharge...

3voto

evgenyl Points 2654

Peut-être que cet article vous aidera :

Si je me souviens bien, 'Nothing' en VB signifie "la valeur par défaut". Pour un type de valeur, c'est la valeur par défaut, pour un type de référence, ce serait null. Ainsi, assigner rien à une structure n'est pas du tout un problème.

3 votes

Cela ne répond pas à la question.

0 votes

Non, cela ne clarifie rien. La question concerne l'opérateur <> en VB et comment il fonctionne sur les types nullables.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X