Gibt es einen Unterschied zwischen return myVar und return (myVar)?

87

Ich habe mir einen Beispiel-C # -Code angesehen und festgestellt, dass ein Beispiel die Rückgabe in () eingeschlossen hat.

Ich habe es immer gerade getan:

return myRV;

Gibt es einen Unterschied:

return (myRV);
chris
quelle

Antworten:

229

UPDATE: Diese Frage war das Thema meines Blogs am 12. April 2010 . Danke für die amüsante Frage!

In der Praxis gibt es keinen Unterschied.

In der Theorie könnte es ein Unterschied. Es gibt drei interessante Punkte in der C # -Spezifikation, an denen dies einen Unterschied darstellen könnte.

Erstens die Konvertierung anonymer Funktionen zum Delegieren von Typen und Ausdrucksbäumen. Folgendes berücksichtigen:

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

F1ist eindeutig legal. Ist F2? Technisch gesehen nein. Die Spezifikation in Abschnitt 6.5 besagt, dass eine Konvertierung von einem Lambda-Ausdruck in einen kompatiblen Delegatentyp erfolgt. Ist das ein Lambda-Ausdruck ? Nein. Es ist ein Ausdruck in Klammern , der einen Lambda-Ausdruck enthält .

Der Visual C # -Compiler macht hier eine kleine Spezifikationsverletzung und verwirft die Klammern für Sie.

Zweite:

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

F3ist legal. Ist F4? Nein. Abschnitt 7.5.3 besagt, dass ein Ausdruck in Klammern möglicherweise keine Methodengruppe enthält. Auch hier verstoßen wir zur Vereinfachung gegen die Spezifikation und erlauben die Konvertierung.

Dritte:

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

F5ist legal. Ist F6? Nein. Die Spezifikation besagt, dass eine Konvertierung von der wörtlichen Null in einen beliebigen Aufzählungstyp erfolgt. " (0)" ist nicht die wörtliche Null, sondern eine Klammer, gefolgt von der wörtlichen Null, gefolgt von einer Klammer. Wir verletzen hier die Spezifikation und erlauben tatsächlich jeden Ausdruck einer Kompilierungszeitkonstante gleich Null und nicht nur eine wörtliche Null.

In jedem Fall erlauben wir Ihnen, damit durchzukommen, auch wenn dies technisch illegal ist.

Eric Lippert
quelle
12
@ Jason: Ich glaube, die Spezifikationsverletzungen in den ersten beiden Fällen sind einfach Fehler, die nie abgefangen wurden. Der anfängliche Bindungsdurchgang war historisch gesehen sehr aggressiv in Bezug auf die vorzeitige Optimierung von Ausdrücken, und eine der Konsequenzen davon ist, dass Klammern sehr früh weggeworfen werden, früher als sie sein sollten. In so ziemlich jedem Fall funktioniert alles, was intuitiv offensichtlich ist, so, wie es sein sollte, also mache ich mir darüber keine großen Sorgen. Die Analyse des dritten Falls ist hier: blogs.msdn.com/ericlippert/archive/2006/03/28/…
Eric Lippert
6
In der Theorie und in der Praxis gibt es einen Unterschied (ich bin mir nicht sicher, ob Mono diese drei Fälle zulässt, und kenne keine anderen C # -Compiler, daher kann es in der Praxis einen Unterschied in der Praxis geben oder auch nicht). Ein Verstoß gegen die C # -Spezifikation bedeutet, dass Ihr Code nicht vollständig portierbar ist. Einige C # -Compiler verstoßen im Gegensatz zu Visual C # in diesen speziellen Fällen möglicherweise nicht gegen die Spezifikation.
Brian
18
@Bruno: Alles was es braucht ist ungefähr acht- oder zehntausend Stunden Studium eines bestimmten Fachs und auch Sie können ein Experte darin sein. Das ist in vier Jahren Vollzeitarbeit problemlos möglich.
Eric Lippert
32
@Anthony: Wenn ich das mache, sage ich den Leuten nur, dass mein Abschluss in Mathematik ist , nicht in Rechnen .
Eric Lippert
7
In der Theorie sind Praxis und Theorie gleich, in der Praxis jedoch nie.
Sagte Ibrahim Hashimi
40

Es gibt Eckfälle, in denen das Vorhandensein von Klammern das Programmverhalten beeinflussen kann:

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

Ich hoffe, Sie werden dies in der Praxis nie sehen.

Vladimir Reshetnikov
quelle
Nicht gerade eine Antwort auf meine Frage, aber trotzdem interessant - danke.
Chris
1
Können Sie hier erklären, was in 2 los ist?
Eric
2
Sie sollten erläutern, warum dieses Verhalten auftritt.
Arturo Torres Sánchez
26

Nein, es gibt keinen anderen Unterschied als syntaktisch.

JaredPar
quelle
3

Eine gute Möglichkeit, Fragen wie diese zu beantworten, besteht darin, Reflector zu verwenden und zu sehen, welche IL generiert wird. Sie können viel über Compiler-Optimierungen und dergleichen lernen, indem Sie Assemblys dekompilieren.

Bryan
quelle
6
Das würde sicherlich die Frage für den einen speziellen Fall beantworten, aber das wäre nicht unbedingt repräsentativ für die Gesamtheit der Situation.
Beska
Nicht zustimmen. Es gibt der Person eine Anweisung, die Frage zu beantworten.
Bryan