141 votes

Pourquoi les instructions d'affectation renvoient-elles une valeur ?

C'est autorisé :

int a, b, c;
a = b = c = 16;

string s = null;
while ((s = "Hello") != null) ;

D'après ce que j'ai compris, l'affectation s = ”Hello”; devrait seulement causer “Hello” à affecter à s mais l'opération ne devrait pas retourner de valeur. Si c'était vrai, alors ((s = "Hello") != null) produirait une erreur, puisque null ne serait comparé à rien.

Quel est le raisonnement qui permet aux instructions d'affectation de retourner une valeur ?

50 votes

Pour référence, ce comportement est défini dans le C# spec : " Le résultat d'une expression d'affectation simple est la valeur affectée à l'opérande de gauche. Le résultat a le même type que l'opérande de gauche et est toujours classé comme une valeur. "

1 votes

@Skurmedel Je ne suis pas le downvoter, je suis d'accord avec vous. Mais peut-être parce qu'on a pensé que c'était une question rhétorique ?

1 votes

En pascal/delphi, l'affectation := ne renvoie rien. Je déteste ça.

180voto

Eric Lippert Points 300275

D'après ce que j'ai compris, l'affectation s = "Hello" ; devrait seulement provoquer l'affectation de "Hello" à s, mais l'opération ne devrait pas renvoyer de valeur.

Votre compréhension est 100% incorrecte. Pouvez-vous expliquer pourquoi vous croyez cette fausse chose ?

Quel est le raisonnement qui permet aux instructions d'affectation de retourner une valeur ?

Tout d'abord, l'affectation déclarations ne produisent pas de valeur. Affectation expressions produire une valeur. Une expression d'affectation est une déclaration légale ; il n'y a qu'une poignée d'expressions qui sont des déclarations légales en C# : les attentes d'une expression, la construction d'instances, l'incrémentation, la décrémentation, l'invocation et les expressions d'affectation peuvent être utilisées là où l'on attend une déclaration.

Il n'existe qu'un seul type d'expression en C# qui ne produit pas une valeur quelconque, à savoir une invocation de quelque chose qui est typée comme renvoyant void. (Ou, de manière équivalente, une attente d'une tâche sans valeur de résultat associée.) Tous les autres types d'expression produisent une valeur, une variable, une référence, un accès à une propriété ou à un événement, etc.

Remarquez que toutes les expressions qui sont légales en tant que déclarations sont utiles pour leurs effets secondaires . C'est le point clé ici, et je pense que c'est peut-être la cause de votre intuition que les affectations devraient être des déclarations et non des expressions. Idéalement, nous devrions avoir exactement un effet secondaire par instruction, et aucun effet secondaire dans une expression. C'est est il est un peu étrange que du code ayant un effet secondaire puisse être utilisé dans un contexte d'expression.

Le raisonnement derrière l'autorisation de cette fonctionnalité est que (1) elle est souvent pratique et (2) elle est idiomatique dans les langages de type C.

On pourrait noter que la question a été posée : pourquoi est-ce idiomatique dans les langages de type C ?

Malheureusement, Dennis Ritchie n'est plus disponible pour poser des questions, mais je pense qu'une mission laisse presque toujours derrière elle la valeur qui vient d'être attribuée dans un registre. Le C est un langage très "proche de la machine". Il semble plausible et conforme à la conception du C qu'il existe une caractéristique du langage qui signifie essentiellement "continuez à utiliser la valeur que je viens d'attribuer". Il est très facile d'écrire un générateur de code pour cette fonction ; il suffit de continuer à utiliser le registre qui a stocké la valeur qui a été assignée.

1 votes

Je ne savais pas que tout ce qui renvoie une valeur (même la construction d'une instance est considérée comme une expression).

9 votes

@user437291 : Si la construction d'une instance n'était pas une expression, vous ne pourriez rien faire avec l'objet construit - vous ne pourriez pas l'assigner à quelque chose, vous ne pourriez pas le passer dans une méthode, et vous ne pourriez appeler aucune méthode sur lui. Ce serait plutôt inutile, n'est-ce pas ?

1 votes

La modification d'une variable n'est en fait "qu'un" effet secondaire de l'opérateur d'affectation, qui, en créant une expression, produit une valeur comme objectif principal.

48voto

Timwi Points 30896

N'avez-vous pas fourni la réponse ? C'est pour permettre exactement le genre de constructions que vous avez mentionnées.

Un cas courant où cette propriété de l'opérateur d'affectation est utilisée est la lecture des lignes d'un fichier...

string line;
while ((line = streamReader.ReadLine()) != null)
    // ...

1 votes

+1 C'est l'une des utilisations les plus pratiques de cette construction.

13 votes

Je l'apprécie vraiment pour les initialisateurs de propriétés vraiment simples, mais il faut faire attention et tracer la limite du "simple" faible pour la lisibilité : return _myBackingField ?? (_myBackingField = CalculateBackingField()); Beaucoup moins de paillettes que la vérification de la nullité et l'affectation.

44voto

Nathan Baulch Points 7994

Mon utilisation préférée des expressions d'affectation est celle des propriétés initialisées paresseusement.

private string _name;
public string Name
{
    get { return _name ?? (_name = ExpensiveNameGeneratorMethod()); }
}

7 votes

C'est pourquoi tant de gens ont suggéré que le ??= la syntaxe.

3 votes

Maintenant que ??= est là, vous pouvez toujours l'utiliser avec une expression d'affectation, ce qui rend les choses encore plus faciles : public string Name => this.name ??= ExpensiveNameGeneratorMethod();

0 votes

C'est triste que ??= n'est disponible que pour .NET Core :(

28voto

Michael Burr Points 181287

D'une part, il vous permet d'enchaîner vos affectations, comme dans votre exemple :

a = b = c = 16;

D'autre part, il vous permet d'assigner et de vérifier un résultat dans une seule expression :

while ((s = foo.getSomeString()) != null) { /* ... */ }

Ces deux raisons sont peut-être douteuses, mais il y a certainement des gens qui aiment ces constructions.

6 votes

+1 Le chaînage n'est pas une fonction essentielle, mais il est agréable de voir qu'il "fonctionne" alors que tant de choses ne le font pas.

0 votes

+1 pour le chaînage également ; j'ai toujours pensé que c'était un cas spécial géré par le langage plutôt que l'effet secondaire naturel des "expressions retournant des valeurs".

2 votes

Il vous permet également d'utiliser l'affectation dans les retours : return (HttpContext.Current.Items["x"] = myvar);

15voto

Martin Törnwall Points 2722

En dehors des raisons déjà mentionnées (enchaînement d'affectations, set-and-test dans les boucles while, etc. correctement utiliser le using déclaration que vous avez besoin de cette fonctionnalité :

using (Font font3 = new Font("Arial", 10.0f))
{
    // Use font3.
}

MSDN déconseille de déclarer l'objet jetable en dehors de l'instruction using, car il restera alors dans le champ d'application même après avoir été éliminé (voir l'article MSDN que j'ai cité).

4 votes

Je ne pense pas qu'il s'agisse vraiment d'un enchaînement de missions en soi.

1 votes

@kenny : Euh... Non, mais je n'ai jamais prétendu que c'était le cas, non plus. Comme je l'ai dit, à part Pour les raisons déjà mentionnées - qui incluent le chaînage d'affectations - l'instruction using serait beaucoup plus difficile à utiliser si l'opérateur d'affectation ne retournait pas le résultat de l'opération d'affectation. Ceci n'est pas du tout lié au chaînage d'affectations.

1 votes

Mais comme Eric l'a noté plus haut, "les instructions d'affectation ne renvoient pas de valeur". Il s'agit en fait de la syntaxe du langage de l'instruction using, la preuve en est que l'on ne peut pas utiliser une "expression d'affectation" dans une instruction using.

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