C'è una differenza tra return myVar e return (myVar)?


87

Stavo guardando qualche esempio di codice C # e ho notato che un esempio racchiudeva il return in ().

Ho sempre fatto:

return myRV;

C'è una differenza facendo:

return (myRV);

Risposte:


229

AGGIORNAMENTO: Questa domanda è stata oggetto del mio blog il 12 aprile 2010 . Grazie per la divertente domanda!

In pratica non c'è differenza.

In teoria potrebbe esserci una differenza. Ci sono tre punti interessanti nella specifica C # in cui questo potrebbe presentare una differenza.

Innanzitutto, la conversione di funzioni anonime per delegare tipi e alberi delle espressioni. Considera quanto segue:

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

F1è chiaramente legale. È F2? Tecnicamente no. La specifica dice nella sezione 6.5 che c'è una conversione da un'espressione lambda a un tipo di delegato compatibile. È un'espressione lambda ? No. È un'espressione tra parentesi che contiene un'espressione lambda .

Il compilatore Visual C # fa una piccola violazione delle specifiche qui e ignora le parentesi per te.

Secondo:

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

F3è legale. È F4? No. La sezione 7.5.3 afferma che un'espressione tra parentesi non può contenere un gruppo di metodi. Ancora una volta, per tua comodità violiamo le specifiche e consentiamo la conversione.

Terzo:

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

F5è legale. È F6? No. La specifica afferma che esiste una conversione dallo zero letterale a qualsiasi tipo enumerato. " (0)" non è lo zero letterale, è una parentesi seguita dallo zero letterale, seguito da una parentesi. Violiamo la specifica qui e in realtà consentiamo qualsiasi espressione di costante di tempo di compilazione uguale a zero , e non solo zero letterale.

Quindi, in ogni caso, ti permettiamo di farla franca, anche se tecnicamente farlo è illegale.


12
@ Jason: credo che le violazioni delle specifiche nei primi due casi siano semplicemente errori che non sono mai stati rilevati. Il passaggio vincolante iniziale storicamente è stato molto aggressivo nell'ottimizzazione prematura delle espressioni, e una delle conseguenze di ciò è che le parentesi vengono eliminate molto presto, prima di quanto dovrebbero essere. In quasi tutti i casi, tutto ciò fa sì che i programmi che sono intuitivamente ovvi funzionino come dovrebbero, quindi non sono molto preoccupato. L'analisi del terzo caso è qui: blogs.msdn.com/ericlippert/archive/2006/03/28/…
Eric Lippert

6
In teoria, in pratica, non v'è una differenza (non sono sicuro se Mono permette a questi 3 casi, e non so di eventuali altri compilatori C #, quindi non può o non può essere una differenza, in pratica, in pratica). La violazione delle specifiche C # significa che il codice non sarà completamente portabile. Alcuni compilatori C # potrebbero, a differenza di Visual C #, non violare le specifiche in quei casi particolari.
Brian

18
@Bruno: bastano otto o diecimila ore di studio di un dato argomento e anche tu puoi essere un esperto in materia. È facilmente realizzabile in quattro anni di lavoro a tempo pieno.
Eric Lippert

32
@ Anthony: quando lo faccio dico alla gente che la mia laurea è in matematica , non in aritmetica .
Eric Lippert

7
In teoria, pratica e teoria sono la stessa cosa, ma in pratica non lo sono mai.
Sayed Ibrahim Hashimi

40

Ci sono casi d'angolo in cui la presenza di parentesi può avere effetto sul comportamento del programma:

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;
}

Spero che non lo vedrai mai in pratica.


Non esattamente una risposta alla mia domanda, ma comunque interessante - grazie.
chris

1
Puoi spiegare cosa sta succedendo in 2 qui?
Eric

2
Dovresti approfondire il motivo per cui si verifica questo comportamento.
Arturo Torres Sánchez


3

Un buon modo per rispondere a domande come questa è usare Reflector e vedere cosa viene generato IL. Puoi imparare molto sulle ottimizzazioni del compilatore e simili decompilando gli assembly.


6
Ciò risponderebbe sicuramente alla domanda per l'unico caso specifico, ma non sarebbe necessariamente rappresentativo dell'intera situazione.
Beska

Disaccordo. Dà alla persona una direzione per rispondere alla domanda.
Bryan
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.