21 votes

Pourquoi le compilateur n'avertit-il pas au moins sur ce == null ?

Pourquoi le compilateur C# ne se plaint-il même pas avec un avertissement pour ce code ?

if (this == null)
{
   // ...
}

Il est évident que la condition jamais être satisfait.

31voto

Mark Rushakoff Points 97350

Parce que vous pourriez surpasser operator == pour qu'il renvoie la valeur "true" dans ce cas.

public class Foo
{
    public void Test()
    {
        Console.WriteLine(this == null);
    }

    public static bool operator ==(Foo a, Foo b)
    {
        return true;
    }

    public static bool operator !=(Foo a, Foo b)
    {
        return true;
    }
}

La course à pied new Foo().Test() imprimera "True" sur la console.

L'autre question qui se pose est la suivante : pourquoi le compilateur n'émet-il pas un avertissement pour ReferenceEquals(this, null) ? Au bas du lien ci-dessus :

Une erreur fréquente dans les surcharges de operator == est d'utiliser (a == b) , (a == null) ou (b == null) pour vérifier l'égalité des références. Il en résulte un appel à la fonction surchargée operator == ce qui provoque une boucle infinie. Utiliser ReferenceEquals ou de transformer le type en Objet, afin d'éviter la boucle.

Cela pourrait être répondu par la réponse de @Aaronaught. Et c'est aussi pour cela que vous devriez faire (object)x == null o ReferenceEquals(x, null) et ne pas faire un simple x == null lorsque vous vérifiez la présence de références nulles. À moins, bien sûr, que vous ne soyez sûr que le fichier == n'est pas surchargé.

25voto

Eric Lippert Points 300275

Wow... Je crois que j'avais honteusement tort

Je ne suis pas d'accord. Je pense que vous avez toujours un bon point de vue.

Le compilateur sait si la comparaison va aller à un opérateur de comparaison défini par l'utilisateur ou non, et le compilateur sait que si ce n'est pas le cas, alors "this" n'est jamais nul.

Et en fait, le compilateur fait de savoir si une expression donnée peut légalement être nulle ou non, afin de mettre en œuvre une optimisation mineure sur les appels de méthodes non virtuelles. Si vous avez une méthode non virtuelle M et que vous dites foo.M(); alors le compilateur génère ceci comme "faire un appel virtuel à M avec le récepteur foo". Pourquoi ? Parce que nous voulons lancer si foo est nul, et un appel virtuel vérifie toujours la nullité du récepteur. Un appel non-virtuel ne le fait pas ; nous devrions le générer comme "vérifier si foo est null, et ensuite faire un appel non-virtuel à M", ce qui est un code plus long, plus lent et plus irritant.

Maintenant, si nous peut s'en tirer sans faire le contrôle de nullité, nous le faisons. Si vous dites this.M() o (new Foo()).M() alors nous ne générons PAS d'appel virtuel. Nous générons l'appel non virtuel sans vérification de la nullité car nous savons qu'il ne peut pas être nul.

Le compilateur dispose donc d'excellentes données pour savoir si une comparaison particulière avec null réussira parfois, toujours ou jamais.

La question qui se pose alors est la suivante : "si le compilateur sait qu'une comparaison particulière n'aboutira jamais, pourquoi ne pas générer un avertissement à ce sujet ?".

Et la réponse est "parfois oui, parfois non".

C'est le cas dans cette situation :

int x = 123;
if (x == null) ...

Il y a un opérateur d'égalité défini sur deux ints nullables. x est convertible en un int nullable. null est convertible en un int nullable. Cet opérateur d'égalité est donc valide, et donc utilisé, et est bien sûr toujours faux. Le compilateur émet un avertissement indiquant que l'expression est toujours fausse.

Cependant, en raison d'un bogue que nous avons accidentellement introduit dans C# 3, ce code ne produit PAS cet avertissement :

Guid x = whatever;
if (x == null) ...

Même chose. L'opérateur d'égalité nullable guid est valide, mais l'avertissement est supprimé. Je ne sais pas si nous avons corrigé ce bogue pour C# 4 ou non. Si ce n'est pas le cas, j'espère que nous l'intégrerons dans un service pack.

Quant à "if (this == null)" je ne sais pas pourquoi nous n'avons pas d'avertissement à ce sujet. Cela semble être un bon candidat pour un avertissement. L'explication la plus probable est de suivre ce syllogisme logique :

  • les avertissements sont des fonctionnalités du compilateur
  • Les fonctionnalités du compilateur doivent être (1) pensées, (2) conçues, (3) implémentées, (4) testées, (5) documentées et (6) livrées avant que vous ne puissiez en profiter.
  • Personne n'a fait tous de ces six éléments nécessaires à cette fonctionnalité ; nous ne pouvons pas livrer des fonctionnalités auxquelles nous n'avons jamais pensé.
  • n'a donc pas de caractéristique de ce type.

Il existe une infinité de fonctions de compilateur auxquelles nous n'avons pas encore pensé ; nous n'avons mis en œuvre aucune d'entre elles.

Une autre raison de ne pas donner d'avertissement ici est que nous essayons de donner des avertissements sur le code qui est à la fois susceptible d'être tapé par accident y presque certainement erronée pour une raison non évidente . Il est peu probable que l'on tape "this == null" par accident, et bien qu'il soit presque certainement erroné, comme vous le notez dans l'énoncé de votre question, il est évidemment erronée.

Comparez cela à notre "guid == null" -- qui est susceptible de se produire par accident, parce qu'un développeur peut accidentellement penser que Guid est un type de référence. En C++, les guid sont généralement transmis par référence, ce qui rend l'erreur facile à commettre. Le code est presque certainement erroné, mais il l'est d'une manière non évidente. C'est donc un bon candidat pour un avertissement. (C'est pourquoi il est si regrettable que ce soit un avertissement en C# 2 mais pas en C# 3, en raison d'un bogue que nous avons introduit).

4voto

Aaronaught Points 73049

En réalité, la condition peut être satisfait du moins dans Visual Studio 2008. Ce comportement a été corrigé dans VS 2010, mais il n'est pas totalement inconcevable qu'il y ait une autre façon de créer une telle condition.

(version abrégée - en C# 3.5, il est légal de faire référence à this à partir d'une fonction anonyme passée en argument du constructeur, mais si vous essayez de l'utiliser, vous constaterez que this est null .)

3voto

Rune FS Points 13350

Bien que le code ci-dessous soit un bâtard, c'est toujours du C#. Et si vous appelez Bar, vous obtiendrez une exception InvalidOperationException avec le message null.

public class Foo
{
    static Action squareIt;
    static Foo() {
        var method = new DynamicMethod(
                                       "TryItForReal",
                                       typeof(void),
                                       Type.EmptyTypes,
                                       typeof(Foo).Module);
        ILGenerator il = method.GetILGenerator();
        il.Emit(OpCodes.Ldnull);
        il.Emit(OpCodes.Call, typeof(Foo).GetMethod("tryit"));
        il.Emit(OpCodes.Ret);
        squareIt = (Action)method.CreateDelegate(typeof(Action));
    }

    public void tryit()
    {
        if (this == null) {
            throw new InvalidOperationException("Was null");
        }
    }

    public void Bar() {
        squareIt();
    }
}

2voto

Kevin Points 57797

Cela correspond également à d'autres avertissements que C# fait (ou ne fait pas d'ailleurs) comme :

if(true)
or 
if(1 == 1)

Ceux-ci auront également toujours le même résultat, quoi qu'il arrive.

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