2009-04-19 5 views
30

Così, quando ho scritto qualcosa di simileQual è la differenza tra la nuova Action() e una lambda?

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

Refactor Pro! Mette in evidenza questa come una creazione delegato ridondante e mi permette di accorciare a

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

e questo di solito funziona alla grande. Solitamente, ma non sempre. Ad esempio, Rhino Mocks ha un metodo di estensione di nome Do:

IMethodOptions<T> Do(Delegate action); 

Qui, passando nella prima versione opere, ma il secondo no. Cosa sta succedendo esattamente sotto le coperte qui?

+4

Il tuo secondo blocco di codice non viene compilato. Ricevo questo messaggio "Impossibile assegnare un'espressione lambda a una variabile locale implicitamente tipizzata". Ma, se sostituisco "var" con "Action", lo fa. –

+1

Sì, hai ragione, non può essere assegnato a una variabile tipizzata implicitamente, lo modificherò. –

risposta

47

La prima versione è effettivamente facendo:

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

Il problema si sta eseguendo in è che il compilatore deve sapere che tipo di delegato (o albero delle espressioni) deve essere convertito nell'espressione lambda. Ecco perché questo:

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

realtà non si compila - potrebbe essere qualsiasi tipo delegato senza parametri e sia alcun valore di ritorno o lo stesso tipo restituito _myMessage (che è presumibilmente string). Per esempio, tutti questi sono validi:

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

Come potrebbe il compilatore C# capire che tipo action doveva essere, se fosse dichiarato con var?

Il modo più semplice per aggirare questo quando si chiama un metodo (per la Rhino Mocks esempio) è quello di lanciare:

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

VB.Net è in grado di aggirare questo generando tipi delegati in tempo reale in base all'utilizzo.Poiché VB distingue già tra le funzioni di restituzione void e non-void (sub e function) rende più facile la differenziazione – JaredPar

+4

"Come potrebbe il compilatore C# capire quale tipo di azione si intende essere, se fosse dichiarata con var?" Semplice: i tipi di funzione dovrebbero essere di tipo strutturale di prima classe, non di questo tipo di materiale delegato. E il codice citato dovrebbe essere annotato come tale. Ma suppongo che non cambierà ora :). – MichaelGG

+1

Penso che sia necessario un paio di parentesi aggiuntive attorno al lambda per fare un cast del genere. –

8

Hai verificato la compilazione della seconda riga? Non dovrebbe essere compilato perché C# non supporta l'assegnazione di un'espressione lambda a una variabile tipizzata implicitamente (CS0815). Questa linea funzionerà in VB.Net anche se supporta la creazione anonima dei delegati (a partire da VB 9.0).

La versione di Rhino Mocks non viene compilata per lo stesso motivo per cui la seconda riga non deve essere compilata. C# non inferirà automaticamente un tipo per un'espressione lambda. Le espressioni Lambda devono essere utilizzate in un contesto in cui è possibile determinare il tipo di delegato che intendono soddisfare. La prima linea funziona alla grande perché il tipo previsto è chiaro: Azione. La versione di Rhino Mocks non funziona perché il Delegato è più simile a un tipo di delegato astratto. Deve essere un tipo di delegato concreto come Action o Func.

Per una discussione dettagliata su questo argomento, si dovrebbe leggere le inserzioni blog di Eric Lippert sul tema: http://blogs.msdn.com/ericlippert/archive/2007/01/11/lambda-expressions-vs-anonymous-methods-part-two.aspx

Problemi correlati