169 votes

Pourquoi l'instruction (j++); était-elle interdite?

Le code suivant est incorrect (voir sur ideone) :

public class Test
{
    public static void Main()
    {
        int j = 5;
        (j++);      // if we remove the "(" and ")" then this compiles fine.
    }
}

error CS0201: Seules les expressions d'assignation, d'appel, d'incrémentation, de décrémentation, d'attendre et de création d'objet peuvent être utilisées comme instruction

  1. Pourquoi le code compile-t-il lorsque nous supprimons les parenthèses ?
  2. Pourquoi ne compile-t-il pas avec les parenthèses ?
  3. Pourquoi C# a-t-il été conçu de cette manière ?

220voto

Eric Lippert Points 300275

Des idées profondes appréciées.

Je ferai de mon mieux.

Comme d'autres réponses l'ont noté, ce qui se passe ici, c'est que le compilateur détecte qu'une expression est utilisée comme une déclaration. Dans de nombreux langages -- C, JavaScript et bien d'autres -- il est tout à fait légal d'utiliser une expression comme une déclaration. 2 + 2; est légal dans ces langages, même si c'est une déclaration qui n'a aucun effet. Certaines expressions sont utiles uniquement pour leurs valeurs, certaines expressions sont utiles uniquement pour leurs effets secondaires (comme un appel à une méthode renvoyant void) et certaines expressions, malheureusement, sont utiles pour les deux. (Comme l'incrémentation.)

Le point important est que les déclarations qui ne consistent qu'en des expressions sont presque certainement des erreurs à moins que ces expressions soient généralement considérées comme plus utiles pour leurs effets secondaires que pour leurs valeurs. Les concepteurs de C# ont souhaité trouver un juste milieu en autorisant les expressions qui étaient généralement considérées comme ayant des effets secondaires, tout en interdisant celles qui sont également généralement considérées comme utiles pour leurs valeurs. Le jeu d'expressions qu'ils ont identifié dans C# 1.0 était les incréments, les décréments, les appels de méthode, les affectations et, de manière quelque peu controversée, les invocations de constructeur.


À PART : On pense normalement qu'une construction d'objet est utilisée pour la valeur qu'elle produit, et non pour l'effet secondaire de la construction ; à mon avis, autoriser new Foo(); est un peu une mauvaise idée. En particulier, j'ai vu ce pattern dans du code du monde réel qui a provoqué un défaut de sécurité :

catch(FooException ex) { new BarException(ex); } 

Il peut être étonnamment difficile de repérer ce défaut si le code est compliqué.


Le compilateur travaille donc pour détecter toutes les déclarations qui consistent en des expressions qui ne sont pas dans cette liste. En particulier, les expressions entre parenthèses sont identifiées comme telles -- des expressions entre parenthèses. Elles ne figurent pas sur la liste des "autorisées en tant qu'expressions de déclaration", donc elles sont interdites.

Tout cela sert à un principe de conception du langage C#. Si vous avez tapé (x++); vous faisiez probablement quelque chose de mal. C'est probablement une faute de frappe pour M(x++); ou quelque chose du genre. Rappelez-vous, l'attitude de l'équipe du compilateur C# n'est pas "pouvons-nous trouver un moyen de faire fonctionner cela ?". L'attitude de l'équipe du compilateur C# est "si le code plausible ressemble à une erreur probable, informons le développeur". Les développeurs C# aiment cette attitude.

Cela dit, il y a en fait quelques cas curieux où la spécification C# implique ou affirme explicitement que les parenthèses sont interdites mais le compilateur C# les autorise quand même. Dans presque tous ces cas, la légère divergence entre le comportement spécifié et le comportement autorisé est totalement inoffensive, donc les rédacteurs du compilateur n'ont jamais corrigé ces petits bugs. Vous pouvez en lire davantage ici :

_

Y a-t-il une différence entre return myVar vs. return (myVar) ?

_

46voto

John Carpenter Points 2455

Dans la spécification du langage C#

Les instructions d'expression sont utilisées pour évaluer des expressions. Les expressions pouvant être utilisées comme instructions incluent les invocations de méthode, les allocations d'objets à l'aide de l'opérateur new, les affectations à l'aide de = et des opérateurs d'affectation composés, les opérations d'incrémentation et de décrémentation à l'aide des opérateurs ++ et -- et les expressions await.

Mettre des parenthèses autour d'une instruction crée une nouvelle expression dite parenthésée. De la spécification :

Une expression parenthésée consiste en une expression enclose entre des parenthèses. ... Une expression parenthésée est évaluée en évaluant l'expression à l'intérieur des parenthèses. Si l'expression à l'intérieur des parenthèses désigne un espace de noms ou un type, une erreur de compilation se produit. Sinon, le résultat de l'expression parenthésée est le résultat de l'évaluation de l'expression contenue.

Comme les expressions parenthésées ne sont pas répertoriées comme une instruction d'expression valide, ce n'est pas une instruction valide selon la spécification. Pourquoi les concepteurs ont choisi de faire de cette façon, c'est à chacun de deviner, mais je parie que c'est parce que les parenthèses ne font aucun travail utile si toute l'instruction est contenue entre parenthèses : stmt et (stmt) sont exactement les mêmes.

19voto

CaldasGSM Points 762

Parce que les crochets autour de i++ créent/définissent une expression... comme le message d'erreur le dit... une simple expression ne peut pas être utilisée comme une instruction.

pourquoi le langage a été conçu de cette manière ? pour éviter les bugs, avoir des expressions trompeuses en tant qu'instructions, qui ne produisent aucun effet secondaire comme avoir le code

int j = 5;
j+1; 

la deuxième ligne n'a aucun effet (mais vous ne l'avez peut-être pas remarqué). Mais au lieu que le compilateur la supprime (car le code n'est pas nécessaire), il vous demande explicitement de la supprimer (pour que vous soyez conscient de l'erreur) OU de la corriger au cas où vous auriez oublié de taper quelque chose.

éditer:

pour rendre la partie sur les crochets plus claire... les crochets en c# (en plus d'autres utilisations, comme les conversions et les appels de fonction), sont utilisés pour regrouper des expressions et retourner une seule expression (composée des sous-expressions).

à ce niveau de code seules les instructions sont autorisées... donc

j++; est une instruction valide car elle produit des effets secondaires

mais en utilisant les crochets vous la transformez en une expression

myTempExpression = (j++)

et cela

myTempExpression;

n'est pas valide car le compilateur ne peut pas garantir que l'expression a un effet secondaire (sans tomber dans le problème du point d'arrêt)..

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