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

87

Je regardais un exemple de code C # et j'ai remarqué qu'un exemple enveloppait le retour entre ().

J'ai toujours juste fait:

return myRV;

Y a-t-il une différence à faire:

return (myRV);
Chris
la source

Réponses:

229

MISE À JOUR: Cette question a fait l'objet de mon blog le 12 avril 2010 . Merci pour la question amusante!

En pratique, il n'y a pas de différence.

En théorie, il pourrait y avoir une différence. Il y a trois points intéressants dans la spécification C # où cela pourrait présenter une différence.

Tout d'abord, la conversion de fonctions anonymes pour déléguer des types et des arborescences d'expressions. Considérer ce qui suit:

Func<int> F1() { return ()=>1; }
Func<int> F2() { return (()=>1); }

F1est clairement légal. Est-ce F2? Techniquement, non. La spécification indique dans la section 6.5 qu'il existe une conversion d'une expression lambda en un type délégué compatible. Est-ce une expression lambda ? Non. C'est une expression entre parenthèses qui contient une expression lambda .

Le compilateur Visual C # effectue une petite violation de spécification ici et supprime la parenthèse pour vous.

Seconde:

int M() { return 1; }
Func<int> F3() { return M; }
Func<int> F4() { return (M); }

F3est légal. Est-ce F4? Non. La section 7.5.3 indique qu'une expression entre parenthèses ne peut pas contenir de groupe de méthodes. Encore une fois, pour votre commodité, nous violons les spécifications et autorisons la conversion.

Troisième:

enum E { None }
E F5() { return 0; }
E F6() { return (0); }

F5est légal. Est-ce F6? Non. La spécification indique qu'il y a une conversion du zéro littéral en n'importe quel type énuméré. " (0)" n'est pas le zéro littéral, c'est une parenthèse suivie du zéro littéral, suivi d'une parenthèse. Nous violons la spécification ici et autorisons en fait toute expression de constante de temps de compilation égale à zéro , et pas seulement zéro littéral.

Donc, dans tous les cas, nous vous permettons de vous en sortir, même si cela est techniquement illégal.

Eric Lippert
la source
12
@Jason: Je pense que les violations de spécifications dans les deux premiers cas sont simplement des erreurs qui n'ont jamais été détectées. La passe de liaison initiale a toujours été très agressive sur l'optimisation prématurée des expressions, et l'une des conséquences de cela est que les parenthèses sont jetées très tôt, plus tôt qu'elles ne devraient l'être. Dans presque tous les cas, tout cela fait que les programmes qui sont intuitivement évidents fonctionnent comme ils le devraient, donc je ne suis pas très inquiet à ce sujet. L'analyse du troisième cas est ici: blogs.msdn.com/ericlippert/archive/2006/03/28
Eric Lippert
6
En théorie, en pratique, il y a une différence (je ne sais pas si Mono autorise ces 3 cas, et je ne connais aucun autre compilateur C #, donc il peut y avoir ou non une différence dans la pratique dans la pratique). La violation de la spécification C # signifie que votre code ne sera pas entièrement portable. Certains compilateurs C # peuvent, contrairement à Visual C #, ne pas enfreindre la spécification dans ces cas particuliers.
Brian
18
@Bruno: Il suffit d'environ huit ou dix mille heures d'étude d'un sujet donné et vous aussi pouvez être un expert en la matière. C'est facilement faisable en quatre ans de travail à temps plein.
Eric Lippert
32
@Anthony: Quand je fais ça, je dis juste aux gens que mon diplôme est en mathématiques , pas en arithmétique .
Eric Lippert
7
En théorie, la pratique et la théorie sont les mêmes mais, en pratique, elles ne le sont jamais.
Sayed Ibrahim Hashimi
40

Il existe des cas d'angle où la présence de parenthèses peut avoir un effet sur le comportement du programme:

1.

using System;

class A
{
    static void Foo(string x, Action<Action> y) { Console.WriteLine(1); }
    static void Foo(object x, Func<Func<int>, int> y) { Console.WriteLine(2); }

    static void Main()
    {
        Foo(null, x => x()); // Prints 1
        Foo(null, x => (x())); // Prints 2
    }
}

2.

using System;

class A
{
    public A Select(Func<A, A> f)
    {
        Console.WriteLine(1);
        return new A();
    }

    public A Where(Func<A, bool> f)
    {
        return new A();
    }

    static void Main()
    {
        object x;
        x = from y in new A() where true select (y); // Prints 1
        x = from y in new A() where true select y; // Prints nothing
    }
}

3.

using System;

class Program
{
    static void Main()
    {
        Bar(x => (x).Foo(), ""); // Prints 1
        Bar(x => ((x).Foo)(), ""); // Prints 2
    }

    static void Bar(Action<C<int>> x, string y) { Console.WriteLine(1); }
    static void Bar(Action<C<Action>> x, object y) { Console.WriteLine(2); }
}

static class B
{
    public static void Foo(this object x) { }
}

class C<T>
{
    public T Foo;
}

J'espère que vous ne verrez jamais cela en pratique.

Vladimir Reshetnikov
la source
Pas exactement une réponse à ma question, mais toujours intéressante - merci.
chris
1
Pouvez-vous expliquer ce qui se passe en 2 ici?
Eric
2
Vous devriez expliquer pourquoi ce comportement se produit.
Arturo Torres Sánchez
26

Non, il n'y a pas de différence autre que syntaxique.

JaredPar
la source
3

Un bon moyen de répondre à des questions comme celle-ci est d'utiliser Reflector et de voir ce que IL est généré. Vous pouvez en apprendre beaucoup sur les optimisations du compilateur et autres en décompilant les assemblys.

Bryan
la source
6
Cela répondrait certainement à la question pour un cas précis, mais ce ne serait pas nécessairement représentatif de l'ensemble de la situation.
Beska
Être en désaccord. Cela donne à la personne une direction pour répondre à la question.
Bryan