2013-05-02 11 views
11

Ho trovato due modi diversi per inizializzare un delegato con un'azione:Azione per delegare: nuova azione o azione di lancio?

Creare una nuova azione o lanciare su Azione.

Delegate foo = new Action(() => DoNothing(param)); 
Delegate bar = (Action)(() => DoNothing(param)); 

C'è una differenza tra queste 2 sintassi?

Quale è migliore e perché?

Edit:
delegato è l'uso in questo esempio perché la sintassi è utile per richiamare metodi come BeginInvoke o invocare con un'espressione lambda, ed è importante per lanciare l'espressione lambda in un'azione

static main 
{ 
    Invoke((Action)(() => DoNothing())); // OK 
    Invoke(new Action(() => DoNothing())); // OK 
    Invoke(() => DoNothing()); // Doesn't compil 
} 

private static void Invoke(Delegate del) { } 

Ma è interessante vedere che il compilatore ha autorizzato questo:

Action action =() => DoNothing(); 
Invoke(action); 
+0

Per la tua modifica: Che significato il contenuto sarebbe nel tuo metodo 'Invoke'' statico'? –

+0

Niente di interessante. Lo scopo della domanda è solo capire la differenza tra le 2 sintassi. La mia vera implementazione è in un'applicazione WPF quando chiamo Dispatcher.Invoke() (http://msdn.microsoft.com/en-us/library/cc647509(v=vs.100).aspx) – Hyralex

+1

Vedo. Dovresti ancora ricordare che un 'Azione' ___is___ un 'Delegato ', poiché' Azione' deriva da' Delegato '. Quindi, anche se usi un overload che accetta un 'Delegate', puoi dargli un' Action'. Esempio: 'Action bar =() => DoNothing (param); someDispatcher.BeginInvoke (bar); '(vedi [' Dispatcher.BeginInvoke'] (http://msdn.microsoft.com/en-us/library/cc190824.aspx)). Aggiunta: in una versione più recente del framework, .NET 4.5, c'è un sovraccarico di 'Dispatcher.Invoke' che richiede' Azione', ma solo per comodità, vedi http://msdn.microsoft.com/en-us/library /hh199416.aspx. –

risposta

10

Non c'è differenza tra queste due istruzioni. In entrambe le istruzioni viene creata una nuova istanza di Action.

Il codice IL sottostante sembra confermarlo.

Programma Console:

class Program 
{ 
    static void Main(string[] args) 
    { 
     Delegate barInit = (Action)(() => DoNothing()); 
     Delegate fooInit = new Action(() => DoNothing()); 
    } 

    private static void DoNothing() { } 
} 

IL Codice:

// First instruction 
IL_0000: ldsfld class [mscorlib]System.Action CodeMachineTest.Program::'CS$<>9__CachedAnonymousMethodDelegate2' 
IL_0005: brtrue.s IL_0018 

IL_0007: ldnull 
IL_0008: ldftn void CodeMachineTest.Program::'<Main>b__0'() 

// Create a new Action instance for the instruction (Action)(() => DoNothing()) 
IL_000e: newobj instance void [mscorlib]System.Action::.ctor(object, native int) 

IL_0013: stsfld class [mscorlib]System.Action CodeMachineTest.Program::'CS$<>9__CachedAnonymousMethodDelegate2' 

IL_0018: ldsfld class [mscorlib]System.Action CodeMachineTest.Program::'CS$<>9__CachedAnonymousMethodDelegate2' 
IL_001d: pop 

// Second instruction 
IL_001e: ldsfld class [mscorlib]System.Action CodeMachineTest.Program::'CS$<>9__CachedAnonymousMethodDelegate3' 
IL_0023: brtrue.s IL_0036 

IL_0025: ldnull 
IL_0026: ldftn void CodeMachineTest.Program::'<Main>b__1'() 
IL_002c: newobj instance void [mscorlib]System.Action::.ctor(object, native int) 
IL_0031: stsfld class [mscorlib]System.Action CodeMachineTest.Program::'CS$<>9__CachedAnonymousMethodDelegate3' 

IL_0036: ldsfld class [mscorlib]System.Action CodeMachineTest.Program::'CS$<>9__CachedAnonymousMethodDelegate3' 
IL_003b: pop 
IL_003c: ret 
2

Secondo me non c'è differenza.

new Action(() => DoNothing(param)); 

Questo solo crea una nuova azione e passa lungo un'espressione lambda, che il compilatore affrontare e fare in modo che tutto è cablato bene.

(Action)(() => DoNothing(param)); 

Ciò funziona perché un metodo lambda come questo non restituisce alcun valore e non accetta parametri, come ad esempio il compilatore può verificare che sia "mappable" per un'azione sulla base del fatto che esso passa attraverso il sistema delegato.

Sono più o meno uno e lo stesso, a seconda di qualsiasi tipo di ottimizzatore del compilatore è difficile dire quale sia più performante, forse dovresti testare le prestazioni e vedere tu stesso?

E 'una domanda interessante e un'esplorazione nel sistema di deleghe e di come LINQ e le espressioni in forma in

new Func<string>(() => "Boo!"); 

è più o meno equivalente a:.

(Func<String>)() => "Boo!"; 

Per quanto mi Mi rendo conto che entrambi finiscono col passare attraverso il sistema delegato a pensare a Invoke() ecc. Sarebbe interessante se tu testassi la performance e avessi condiviso i risultati.

+1

Ho fatto qualche test e non c'è differenza. Dopo lo smontaggio, IL e codice macchina sono gli stessi. – Hyralex

+1

Non è particolarmente interessante, sono solo due sintassi che portano allo stesso IL. Quindi questo non ha alcuna relazione con le prestazioni, quindi cosa vuoi che condivida? Leggi la specifica del linguaggio C# se desideri dettagli. La prima espressione è in _7.6.10.5 Delega di creazione espressioni_, e la seconda è in _6.5 Conversioni di funzione anonima_. I numeri di sezione provengono dalla versione 5.0 del documento. Si noti che 7.6.10.5 afferma chiaramente che viene elaborato allo stesso modo di 6.5. –

0

Non c'è differenza, sono solo due sintassi per lo stesso. Personalmente, io uso l'ultimo, perché è più breve.

Ma perché è necessaria una variabile di tipo Delegate? Nella maggior parte dei casi si desidera che la variabile abbia lo stesso tipo come esempio, e quindi è possibile utilizzare

var bar = (Action)(() => DoNothing(param)); 

o

Action bar =() => DoNothing(param); 

invece di

Delegate bar = (Action)(() => DoNothing(param)); // (from your question) 
+0

Alcuni metodi come Invoke o BeginInvoke richiedono un delegato ed è necessario eseguire il cast o creare un'azione per utilizzare l'espressione lambda. – Hyralex

+0

@Hyralex Ma tutti i tipi di delegati concreti, incluso 'System.Action', derivano (tramite' System.MulticastDelegate') da 'System.Delegate'. Pertanto i metodi 'Invoke' e' BeginInvoke' e altri membri sono ereditati. Quindi potresti dire ad es. 'Action bar =() => DoNothing (param); bar.BeginInvoke (...); '. Quindi non capisco. –

+0

Sto parlando di BeginInvoke e Invoke di oggetti esistenti come Dispatcher. Ho modificato la mia domanda per dare più esempio. – Hyralex