2009-04-19 5 views
30

Así que cuando escribo algo como esto¿Cuál es la diferencia entre la nueva Acción() y una lambda?

Action action = new Action(()=>_myMessage = "hello"); 

Refactor Pro! Destaca esto como una creación de delegado redundante y me permite acortarlo a

Action action =() => _myMessage="hello"; 

Y esto generalmente funciona genial. Generalmente, pero no siempre. Por ejemplo, burla de Rhino tiene un método de extensión llamado Do:

IMethodOptions<T> Do(Delegate action); 

Aquí, pasando en la primera versión trabajos, pero el segundo no lo hace. ¿Qué está pasando exactamente debajo de las coberturas aquí?

+4

Su segundo bloque de código no compila. Aparece el mensaje "No se puede asignar la expresión lambda a una variable local implícitamente tipada". Pero, si reemplazo "var" por "Acción", lo hace. –

+1

Sí, tienes razón, no se puede asignar a una variable implícitamente tipada, la editaré. –

Respuesta

47

La primera versión está haciendo con eficacia:

Action tmp =() => _myMessage = "hello"; 
var action = new Action(tmp); 

El problema que se está ejecutando en es que el compilador debe saber qué tipo de delegado (o árbol de expresiones) debe convertir la expresión lambda. Es por eso que este:

var action =() => _myMessage="hello"; 

realidad no se compila - que podría ser cualquier tipo delegado sin parámetros y, o bien no devuelve ningún valor o el mismo tipo de retorno como _myMessage (que es de suponer string). Por ejemplo, todos estos son válidas:

Action action =() => _myMessage="hello"; 
Func<string> action =() => _myMessage="hello"; 
MethodInvoker action =() => _myMessage="hello"; 
Expression<Action> =() => _myMessage="hello"; 
// etc 

¿Cómo puede el compilador de C# averiguar qué tipo action estaba destinado a ser, si se declara con var?

La forma más sencilla de conseguir alrededor de esto cuando se llama a un método (por ejemplo, su burla de Rhino) es fundido:

methodOptions.Do((Action) (() => _myMessage = "hello")); 
+3

VB.Net es capaz de evitar esto generando tipos de delegados sobre la marcha en función del uso.Debido a que VB ya diferencia entre funciones de retorno nulas y no válidas (sub y función), facilita la diferenciación – JaredPar

+4

"¿Cómo podría el compilador de C# determinar qué tipo de acción debía ser, si se declarase con var?" Simple: los tipos de funciones deberían ser tipos estructurales de primera clase, no esta cosa delegada nombrada. Y el código citado debe anotarse como tal. Pero supongo que eso no cambiará ahora :). – MichaelGG

+1

Creo que necesitas un par adicional de paréntesis alrededor de la lambda para hacer tal molde. –

8

¿Ha verificado que realmente compila la segunda línea? No debe compilarse porque C# no admite la asignación de una expresión lambda a una variable implícitamente tipada (CS0815). Sin embargo, esta línea funcionará en VB.Net porque admite la creación de delegados anónimos (comenzando en VB 9.0).

La versión de Rhino Mocks no se compila por la misma razón que la segunda línea no debe compilarse. C# no deducirá automáticamente un tipo para una expresión lambda. Las expresiones Lambda deben usarse en un contexto donde sea posible determinar el tipo de delegado que se pretende que cumplan. La primera línea funciona bien porque el tipo previsto es claro: Acción. La versión de Rhino Mocks no funciona porque Delegate es más parecido a un tipo de delegado abstracto. Debe ser un tipo de delegado concreto como Action o Func.

Para una discusión detallada sobre este tema, usted debe leer las entradas de blog de Eric Lippert sobre el tema: http://blogs.msdn.com/ericlippert/archive/2007/01/11/lambda-expressions-vs-anonymous-methods-part-two.aspx

Cuestiones relacionadas