2015-10-22 7 views
7

Mi sono imbattuto in una situazione in cui ho bisogno di una certa conoscenza.Azione uguale a Func <TResult>?

Di seguito si riporta il codice:

// A function to match the delegate 
public static int DoSomething() 
{ 
    Console.WriteLine("i am called"); 
    return 1; 
} 

// Usage 
Action action =() => DoSomething(); 
Func<int> func =() => DoSomething(); 
action(); 
func(); 

mia comprensione di Action usato per essere che dovrebbe corrispondere a un delegato che accetta nessun parametro e non restituisce nulla.

E per Func<int> che deve corrispondere a un delegato che non accetta parametri e restituisce un int.

DoSomething metodo restituisce un numero intero, quindi la mia domanda: () => DoSomething() è un delegato che restituisce un int. Func funziona come previsto, ma non lo è Action. Perché? Cosa non riesco a capire qui?

Il codice viene compilato e viene eseguito correttamente, entrambi in uscita i am called. Quello che voglio sapere è che perché Action action =() => DoSomething(); non è un errore di compilazione?

+3

* >> ma l'azione non *: come funziona esattamente? –

+0

la riga di codice 'Azione action = (x) => DoSomething (x);', non dovrebbe essere un errore di compilazione perché 'Azione ' richiede la corrispondenza con un delegato che non restituisce un valore? – singsuyash

+4

@singsuyash il compilatore C# è abbastanza intelligente da capire che '(x) => DoSomething (x)' significa cose diverse a seconda del contesto. Quando lo si usa per assegnare una variabile 'Action', genera un' Action', non un 'Func' e ignora il risultato di ritorno di' DoSomething (x) '. –

risposta

10

Quello che voglio sapere è che il motivo per cui Action action =() => DoSomething(); non è un errore di tempo di compilazione?

Compila perché hai un'espressione lambda che chiama il metodo ma ignora il risultato. Non è possibile utilizzare una conversione del gruppo di metodi, ad es.

// Compile-time failure 
// error CS0407: 'int Test.DoSomething()' has the wrong return type 
Action action = DoSomething; 

(la stessa conversione metodo di gruppo per Func<Action, int> va bene.)

Ma invece, si sta facendo qualcosa di più simile a questo:

Action action = DoSomethingAndIgnoreResult; 
... 
private static void DoSomethingAndIgnoreResult() 
{ 
    DoSomething(); // Hey, I'm ignoring the result. That's fine... 
} 
+0

Potrebbe essere interessante notare che l'errore per la conversione del gruppo metodo è qualcosa come "Un metodo previsto con la firma 'void DoSomething (oggetto)' –

+0

@DaveZych: Fatto, con il vero messaggio di errore che ricevo. –

+0

Grazie @JonSkeet per la risposta aggiornata :) e sì, stai attento a non cambiarlo in futuro. – singsuyash

3

Il compilatore C# è abbastanza intelligente da capire che () => DoSomething() significa cose diverse a seconda del contesto. Quando lo si utilizza per assegnare una variabile Action, viene generato un Action (anziché Func<int>) che ignora il risultato di ritorno di DoSomething().

8

Action action =() => DoSomething(); è equivalente a
Action action =() => { DoSomething(); };

Func<int> func =() => DoSomething(); è equivalente a
Func<int> func =() => { return DoSomething(); };

0

Tutte le vostre comprensioni sono corrette. È il tuo uso specifico che potrebbe causare confusione. Sia Func che Action vanno bene. Entrambe le chiamate vanno bene. Mi sembra il caso che può illuminare il vostro problema è:

var x = action(5); // NOT ok var y = func(5); // ok

Il tuo codice di esempio ignora semplicemente il valore di ritorno, che è il motivo per cui sembra come se fossero la stessa cosa. Non è diverso da

void Foo1(int x) { return; } void Foo2(int x) { return 1; } Foo1(5); Foo2(5);

1

DoSomething metodo restituisce un numero intero, da qui la mia domanda: (x) => DoSomething(x) è un delegato che accetta un object e restituisce un int.Func funziona come previsto, ma non lo è Action. Perché? Cosa non riesco a capire qui?

Il difetto nella tua comprensione è proprio qui: (x) => DoSomething(x) non ha un tipo. Non è niente. Il compilatore richiede un contesto per capire quale sia il tipo di questo. Di per sé un lambda non è nulla in particolare, per questo non è possibile utilizzare var con un'espressione lambda: il compilatore non sa quale tipo deve essere il lambda, quindi non può dedurre il tipo.

Per esempio, (x) => DoSomething(x) potrebbe anche essere un albero di espressione:

Expression<Func<object, int>> e = (x) => DoSomething(x) 

Quindi stai dicendo al compilatore come interpretare un lambda in base al tipo si sta assegnandolo a.