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.