web-dev-qa-db-fra.com

Pourquoi ce code n'est-il pas inaccessible?

J'ai trouvé un cas où j'ai du code que je crois inaccessible et qui n'est pas détecté. Aucun avertissement n'est émis ni par le compilateur ni par Visual Studio.

Considérons ce code:

enum Foo { A, B, C }
class Bar { public Foo type; }

static class Program
{
    private static void Main()
    {
        var bar = new Bar { type = Foo.A };

        if (bar.type == Foo.B)
        {
            Console.WriteLine("lol");
        }
    }
}

De toute évidence, le programme n’imprimera pas "lol" car la condition dans la déclaration if est fausse. Je ne comprends pas pourquoi un avertissement n'est pas émis pour le code inaccessible. Ma seule hypothèse est que cela pourrait potentiellement être accessible si vous avez une situation de concurrence critique dans un programme multithread. Est-ce correct?

49
Michele Ippolito

L'analyse statique ne peut en faire que beaucoup et ne marquera le code comme inaccessible que si elle peut prouver qu'une valeur ne peut pas être modifiée. Dans votre code, ce qui se passe à l'intérieur de Bar n'entre pas dans le cadre du flux de méthodes et ne peut pas faire l'objet d'un raisonnement statique. Que se passe-t-il si le constructeur de Bar lance un thread qui redéfinit la valeur de type sur B? Le compilateur ne peut pas le savoir car, encore une fois, les éléments internes de Bar ne sont pas liés à la méthode.

Si votre code vérifiait la valeur d'une variable locale, le compilateur pourrait alors savoir s'il était impossible de le modifier. Mais ce n'est pas le cas ici.

134

Le spécification C # dit,

La première instruction incorporée d'une instruction if est accessible si l'instruction if est accessible et si l'expression booléenne n'a pas la valeur constante false.

et, concernant expressions constantes ,

Une expression constante doit être le littéral nul ou une valeur de l'un des types suivants: sbyte, octet, short, ushort, int, uint, long, ulong, char, float, double, décimal, bool, objet, chaîne type d'énumération.

Seules les constructions suivantes sont autorisées dans les expressions constantes:

  • Littéraux (incluant le null littéral).
  • Références aux membres const des types class et struct.
  • Références aux membres des types d'énumération.
  • Références aux paramètres const ou aux variables locales
  • Les sous-expressions entre parenthèses, qui sont elles-mêmes des expressions constantes.
  • Cast expressions, à condition que le type de cible soit l’un des types énumérés ci-dessus. expressions cochées et non cochées
  • Expressions de valeur par défaut
  • Le prédéfini +, , !, et ~ opérateurs unaires.
  • Le prédéfini +, , *, /, %, <<, >>, &, |, ^, &&, ||, ==, !=, <, >, <=, et >= opérateurs binaires, à condition que chaque opérande soit d'un type énuméré ci-dessus.
  • Le ?: opérateur conditionnel.

Les expressions d'accès membres ne figurent pas dans cette liste. Par conséquent, l'expression booléenne n'est pas constante. Ainsi, le corps du bloc if est accessible.

27
Rawling

Parce qu'une telle garantie ne peut être faite au moment de la compilation. Considérez cette classe de bar alternative

class Bar
{
   Random random = new Random();
   Array Foos = Enum.GetValues(typeof(Foo));

    private Foo _type;
    public Foo type
    {
        get { return _type; }
        set
        {
            _type = (Foo)Foos.GetValue(random.Next(3));
        }
    }
}

Veuillez noter que "accessible" est défini au niveau de la fonction. Il est interdit d'accéder à l'extérieur de la fonction testée, même s'il est sécuritaire de le faire.

9
Ivan Anatolievich

L'avertissement que vous attendez n'est pas mis en œuvre car ce n'est pas un avertissement utile.

Dans les applications du monde réel, le compilateur est très souvent confronté à un code qu'il peut totalement prouver qu'il est inaccessible, peut-être même quelque chose d'aussi chauve que

static class Program
{
    private static void Main()
    {
        if (false)
        {
            Console.WriteLine("lol");
        }
    }
}

Je n'ai pas de compilateur C # sur cet ordinateur, mais je parie que rien ne le prévient non plus. En effet, lorsque vous mettez if (false) { ... } autour d'un bloc de code, vous l'avez fait exprès, peut-être pour désactiver quelque chose temporairement pour une expérience. Vous en tenir à cela ne serait pas utile.

Le plus commun est que ce n'est pas un littéral false, c'est une constante de compilation que le système de compilation définira sur true ou sur false selon la configuration; vous voulez que le compilateur supprime le code inaccessible dans une construction mais pas dans l'autre, et vous ne voulez pas de plaintes de toute façon.

Encore plus commun que cela est pour les premières optimisations telles que l'inligne et la propagation constante à découvrir qu'un conditionnel est toujours faux; supposons que vous ayez quelque chose comme

static class Program
{
    private static void Fizz(int i)
    {
        if (i % 3 == 0) {
            Console.WriteLine("fizz");
        } else {
            Console.WriteLine(i);
        }
    }

    private static void Main()
    {
        Fizz(4);
    }
}

Vous ne voudriez évidemment pas vous faire dire qu'un côté du conditionnel à l'intérieur de Fizz () était inaccessible simplement parce qu'il n'a été appelé qu'avec l'argument 4 dans ce programme.

2
zwol