web-dev-qa-db-fra.com

Pourquoi était la déclaration (j ++); interdit?

Le code suivant est faux (voyez-le sur ideone ):

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

erreur CS0201: seules les expressions d'affectation, d'appel, d'incrémentation, de décrémentation, d'attente et de nouvel objet peuvent être utilisées comme instruction

  1. Pourquoi le code est-il compilé 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 façon?
169
user10607

Des idées profondes appréciées.

Je ferai de mon mieux.

Comme d'autres réponses l'ont fait remarquer, le compilateur détecte qu'un expression est utilisé comme une déclaration. Dans de nombreuses langues - C, JavaScript et bien d’autres -, il est parfaitement légal d’utiliser une expression comme déclaration. 2 + 2; Est légal dans ces langues, même s'il s'agit d'une déclaration sans effet. Certaines expressions ne sont utiles que pour leurs valeurs, d'autres uniquement pour leurs effets secondaires (comme un appel à une méthode de renvoi de vide) et certaines expressions, malheureusement, sont utiles pour les deux. (Comme incrément.)

Le point le plus clair: les énoncés composés uniquement d'expressions sont presque certainement des erreurs à moins que ces expressions ne soient généralement considérées comme plus utiles pour leurs effets secondaires que leurs valeurs. Les concepteurs de C # souhaitaient trouver un terrain d'entente, en permettant des expressions généralement considérées comme ayant un effet secondaire, tout en interdisant celles qui sont généralement considérées comme utiles pour leurs valeurs. Les expressions qu’ils ont identifiées dans C # 1.0 sont les incréments, les décréments, les appels de méthodes, les affectations et, de manière quelque peu controversée, les invocations de constructeurs.


EN OUTRE: On pense généralement 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 un problème. En particulier, j'ai observé ce modèle de code du monde réel qui provoquait un défaut de sécurité:

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

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


Par conséquent, le compilateur détecte toutes les instructions composées d'expressions ne figurant pas dans cette liste. En particulier, les expressions entre parenthèses sont identifiées uniquement comme des expressions entre parenthèses. Ils ne figurent pas sur la liste des "expressions autorisées", ils sont donc interdits.

Tout cela est au service d'un principe de conception du langage C #. Si vous avez tapé (x++);, Vous avez probablement mal agi . C'est probablement une faute de frappe pour M(x++); ou quelque chose de juste. N'oubliez pas que l'attitude de l'équipe du compilateur C # n'est pas " pouvons-nous trouver un moyen de faire en sorte que cela fonctionne?" L'attitude de l'équipe du compilateur C # est " si le code plausible a l'air comme une erreur probable, informons le développeur ". Les développeurs C # aiment cette attitude.

Cela dit, il existe en fait quelques cas étranges où la spécification C # fait implique ou déclare clairement que les parenthèses sont interdites mais que le compilateur C # les autorise quand même. Dans presque tous les cas, la différence mineure entre le comportement spécifié et le comportement autorisé est totalement inoffensive. Par conséquent, les auteurs du compilateur n'ont jamais corrigé ces petits bogues. Vous pouvez lire sur ceux-ci ici:

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

220
Eric Lippert

Dans le spécification du langage C #

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

Le fait de placer des parenthèses autour d'une instruction crée une nouvelle expression appelée parenthèse. De la spécification:

Une expression entre parenthèses consiste en une expression entre parenthèses. ... Une expression entre parenthèses est évaluée en évaluant l'expression entre parenthèses. Si l'expression entre parenthèses désigne un espace de noms ou un type, une erreur de compilation survient. Sinon, le résultat de l'expression parenthesized est le résultat de l'évaluation de l'expression contenue.

Etant donné que les expressions entre parenthèses ne sont pas répertoriées en tant qu'énoncé d'expression valide, il ne s'agit pas d'une instruction valide selon la spécification. Les concepteurs ont choisi de le faire de cette façon, mais tout dépend de mon point de vue, car les parenthèses ne fonctionnent pas si toutes les instructions sont entre parenthèses: stmt et (stmt) sont exactement les mêmes.

46
Frank Bryce

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

pourquoi le langage a été conçu pour être de cette façon? pour éviter les bugs, ayant des expressions trompeuses en tant qu'énoncés, 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 n'avez peut-être pas remarqué). Mais au lieu que le compilateur le supprime (car le code n’est pas nécessaire), il vous demande explicitement de le supprimer (pour que vous soyez au courant ou pour l’erreur) OR corrigez-le au cas où vous auriez oublié de le faire.) tapez quelque chose.

edit:

pour rendre plus claire la partie concernant les crochets. Les crochets en c # (outre d’autres utilisations, telles que cast et call function), sont utilisés pour regrouper des expressions et renvoyer une seule expression (marque des sous-expressions).

à ce niveau de code, seuls les motifs sont autorisés ..

j++; is a valid statement because it produces side effects

mais en utilisant le crochet, vous le transformez en expression

myTempExpression = (j++)

et ça

myTempExpression;

n’est pas valide car le compilateur ne peut pas s’assurer que l’expression est un effet secondaire (non sans que le problème s’arrête).

19
CaldasGSM