2010-02-02 13 views

Antwort

227

UPDATE: Diese Frage war the subject of my blog on 12 April 2010. Danke für die amüsante Frage!

In der Praxis gibt es keinen Unterschied.

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

Zuerst Umwandlung von anonymen Funktionen in Delegattypen und Ausdrucksbäume. Betrachten Sie folgendes:

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

F1 ist eindeutig legal. Ist F2? Technisch, nein. The spec sagt in Abschnitt 6.5, dass es eine Umwandlung von einem Lambda-Ausdruck zu einem kompatiblen Delegattyp gibt. Ist das ein Lambda-Ausdruck? Nein. Es ist ein geklammerter Ausdruck, der einen Lambda-Ausdruck enthält.

Der Visual C# -Compiler macht hier eine kleine Spec-Verletzung und verwirft die Klammer für Sie.

Zweitens:

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

F3 ist legal. Ist F4? Nein. Abschnitt 7.5.3 besagt, dass ein eingeklammerter Ausdruck keine Methodengruppe enthalten darf. Auch hier verletzen wir die Spezifikation und erlauben die Konvertierung.

Drittens:

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

F5 ist legal. Ist F6? Nein. Die Spezifikation besagt, dass es eine Konvertierung von der Literal-Null in einen beliebigen Aufzählungstyp gibt. "(0)" ist nicht das Literal Null, es ist eine Klammer gefolgt von dem Literal Null, gefolgt von einer Klammer. Wir verletzen die Spezifikation hier und erlauben tatsächlich keine Kompilierung Zeitkonstante Ausdruck gleich Null, und nicht nur literal Null.

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

+2

@Eric Lippert: Warum die Spec-Verletzungen? Ich sehe, dass Sie aus Bequemlichkeit sagen, aber wie wiegen Sie die Balance zwischen Verletzung der Spezifikation und Bequemlichkeit? War es ein Fehler, der zur Bequemlichkeit wurde? :-) – jason

+0

Ich glaube, das letzte Wort, das du haben wolltest * illegal *? –

+12

@Jason: Ich glaube, die Spec-Verletzungen in den ersten beiden Fällen sind einfach Fehler, die nie abgefangen wurden. Der anfängliche Bindungspass war in der Vergangenheit sehr aggressiv, Ausdrücke vorzeitig zu optimieren, und eine der Konsequenzen davon ist, dass Klammern sehr früh weggeworfen werden, früher als sie sein sollten. In fast jedem Fall macht das alles Programme, die intuitiv offensichtlich funktionieren, so, wie sie sollten, also mache ich mir keine großen Sorgen darüber. Die Analyse des dritten Falles ist hier: http://blogs.msdn.com/ericlippert/archive/2006/03/28/the-root-of-all-evil-part-one.aspx –

26

Nein, es gibt keinen Unterschied anders als syntaktische.

3

Eine gute Möglichkeit, Fragen wie diese zu beantworten, ist Reflector zu verwenden und zu sehen, was IL generiert wird. Sie können viel über Compiler-Optimierungen lernen, zum Beispiel durch das Dekompilieren von Assemblys.

+6

Das würde sicherlich die Frage für den einen spezifischen Fall beantworten, aber das würde nicht unbedingt für die Gesamtheit der Situation repräsentativ sein. – Beska

+0

Nicht einverstanden. Es gibt der Person eine Richtung, um die Frage zu beantworten. – Bryan

40

Es gibt Ecke Fälle, wenn das Vorhandensein von Klammern Wirkung auf das Programmverhalten haben 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; 
} 

Hoffe, du wirst neve Das sehe ich in der Praxis.

+0

Nicht gerade eine Antwort auf meine Frage, aber immer noch interessant - danke. – chris

+1

Kannst du erklären, was hier in 2 passiert? – Eric

+1

Sie sollten näher erläutern, warum dieses Verhalten auftritt. –

Verwandte Themen