2010-02-02 20 views

Respuesta

227

ACTUALIZACIÓN: Esta pregunta era the subject of my blog on 12 April 2010. Gracias por la divertida pregunta!

En la práctica, no hay diferencia.

En teoría podría haber una diferencia. Hay tres puntos interesantes en la especificación C# donde esto podría presentar una diferencia.

En primer lugar, la conversión de funciones anónimas para delegar tipos y árboles de expresión. Considere lo siguiente:

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

F1 es claramente legal. ¿Es F2? Técnicamente, no. The spec dice en la sección 6.5 que hay una conversión de expresión lambda a un tipo de delegado compatible. ¿Es eso una expresión lambda? No. Es una expresión entre paréntesis que contiene una expresión lambda .

El compilador de Visual C# hace una pequeña violación de la especificación aquí y descarta el paréntesis para usted.

Segundo:

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

F3 es legal. ¿Es F4? No. La sección 7.5.3 establece que una expresión entre paréntesis puede no contener un grupo de métodos. Nuevamente, para su conveniencia, violamos las especificaciones y permitimos la conversión.

Tercero:

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

F5 es legal. ¿Es F6? No. La especificación establece que hay una conversión del cero literal a cualquier tipo enumerado. "(0)" no es el cero literal, es un paréntesis seguido del cero literal, seguido de un paréntesis. Violamos la especificación aquí y de hecho permitimos cualquier expresión de constante de tiempo de compilación igual a cero, y no solo literalmente cero.

Por lo tanto, en todos los casos, le permitimos salirse con la suya, aunque técnicamente hacerlo es ilegal.

+2

@Eric Lippert: ¿Por qué las violaciones de las especificaciones? Veo que dices por conveniencia, pero ¿cómo pesas el equilibrio entre violar las especificaciones y ser conveniente? ¿Fue un error que se convirtió en una característica por conveniencia? :-) – jason

+0

Creo que la última palabra que pretendías ser * ilegal *? –

+12

@Jason: Creo que las violaciones de las especificaciones en los primeros dos casos son simplemente errores que nunca se detectaron. Históricamente, el paso de enlace inicial ha sido muy agresivo con respecto a la optimización prematura de las expresiones, y una de las consecuencias es que los paréntesis se descartan muy temprano, antes de lo que deberían ser. En casi todos los casos, todo lo que hace es hacer que los programas que son intuitivamente obvios funcionen como deberían, así que no estoy muy preocupado por eso. El análisis del tercer caso está aquí: http://blogs.msdn.com/ericlippert/archive/2006/03/28/the-root-of-all-evil-part-one.aspx –

26

No, no hay ninguna diferencia que no sea sintáctica.

3

Una buena manera de responder preguntas como esta es utilizar Reflector y ver qué IL se genera. Puede aprender mucho acerca de las optimizaciones del compilador y similares mediante la descompilación de ensamblados.

+6

Sin duda respondería a la pregunta para el caso específico, pero eso no sería necesariamente representativo de la totalidad de la situación. – Beska

+0

No estoy de acuerdo. Le da a la persona una dirección para responder la pregunta. – Bryan

40

Hay casos extremos cuando la presencia de paréntesis, puede tener efecto sobre el comportamiento del programa:

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

Espero que nunca neve Ver esto en la práctica.

+0

No es exactamente una respuesta a mi pregunta, pero sigue siendo interesante, gracias. – chris

+1

¿Puedes explicarnos qué está pasando en 2 aquí? – Eric

+1

Debe explicar por qué ocurre este comportamiento. –

Cuestiones relacionadas