2012-02-22 12 views
5

Vorrei sapere se esiste un sovraccarico dovuto all'utilizzo di metodi anonimi durante la creazione di un worker in background.C'è qualche sovraccarico nell'uso di metodi anonimi?

ad esempio:

public void SomeMethod() 
{ 
    BackgroundWorker worker = new BackgroundWorker(); 
    worker.DoWork += (sender, e) => 
    { 
     //large amount of code 
    } 

    worker.RunWorkerAsync(); 
} 

Sarebbe l'esempio di cui sopra essere migliore o peggiore di definire il //large amount of code in un metodo separato?

Esiste un sovraccarico per definire in linea il metodo di lavoro in background, in particolare se viene chiamato spesso SomeMethod()?

+0

Se c'è una grande quantità di codice, probabilmente si vuole refactoring in una piccola quantità di codice chiamando altri metodi ... – Chris

risposta

4

C'è una piccola differenza nel modo in cui i metodi denominati e i metodi anonimi vengono gestiti quando si crea un delegato da essi.

I delegati per i metodi anonimi sono memorizzati nella cache, quindi è presente un piccolo sovraccarico per verificare se il delegato esiste già nella cache. D'altra parte, se si esegue il metodo più di una volta, riutilizzerà il delegato memorizzato nella cache invece di crearne uno nuovo.

I delegati per i metodi denominati non vengono memorizzati nella cache, quindi verrà creato ogni volta.

A parte questo non c'è differenza. Il metodo anonimo verrà creato in fase di compilazione ed esiste nel codice come un normale metodo, solo con un nome che solo il compilatore conosce.

1

Ogni volta che un metodo anonimo (incl lambda) si chiude sulle variabili, il compilatore crea una classe per contenere queste variabili. Ogni volta che viene creato il delegato è anche una nuova istanza di questa classe. Questo ovviamente aggiunge lavoro extra per il runtime, ma è generalmente trascurabile nella maggior parte delle situazioni.

+0

Quindi presumo che se non vi è alcuna chiusura che non creerà la classe in modo che non farà alcuna differenza? – Chris

+0

@Chris Sì, il compilatore è abbastanza intelligente da creare un tipo anonimo (per mantenere il metodo anonimo più le variabili chiuse) solo quando è richiesto. –

+0

La classe di chiusura non viene creata quando si crea il delegato. Viene creato prima di utilizzare il closed-over locale, perché l'accesso ai locali nel metodo "genitore" deve effettivamente utilizzare la classe di chiusura. – svick

0

L'ho provato l'altro giorno (tramite l'uso della classe StopWatch). Per quanto ho potuto dire non vi era alcuna differenza in termini di prestazioni tra invocando un metodo direttamente ...

SomeMethod(); 

... o attraverso un metodo anonimo ...

() => SomeMethod(); 
+2

Il compilatore ottimizzerà la necessità di creare una classe anonima per contenere il metodo anonimo poiché nessuna variabile viene chiusa. –

+0

Interessante, pensavo che il compilatore potesse avere una mano in questo. Due domande: se il corpo del metodo anonimo fa riferimento a variabili al di fuori del suo ambito, ciò introdurrà un sovraccarico? E, in caso affermativo, quando si incontrerebbe quel sovraccarico? Durante la creazione o l'esecuzione del metodo anonimo? – Moonshield

0

E 'principalmente interessano la leggibilità - grandi quantità di codice in un unico luogo sono quasi mai bene ;-)

in termini di prestazioni vedere When is optimization premature?

2

In primo luogo, probabilmente non si dovrebbe inserire molto codice in un metodo anonimo. Sarebbe più leggibile se si crea un metodo separato per quello, o ancora meglio, diversi metodi.

Come per l'IL generato, se il lambda non si chiude su nessuna variabile, quindi il codice IL generato è lo stesso come se si inserisse il codice nel metodo con nome normale (eccetto che il metodo generato ha un nome indicibile) .

D'altra parte, se si chiude una variabile, il compilatore crea una classe di chiusura per contenere quella variabile in un campo. E l'accesso al campo è leggermente più costoso dell'accesso alle variabili locali.

Per riassumere, se si chiudono alcune variabili, è presente un piccolo sovraccarico (inclusi altri oggetti che devono essere raccolti con la garbage collection). Nella maggior parte delle situazioni, questo non ha importanza e preoccuparsi di ciò sarebbe un'ottimizzazione prematura. Ma se pensi che sia importante, dovresti profilare il codice.

0

Questo è ciò che ha detto decompilatore:

[CompilerGenerated] 
private static DoWorkEventHandler CS$<>9__CachedAnonymousMethodDelegate1; 

[CompilerGenerated] 
private static void <SomeMethod1>b__0(object sender, DoWorkEventArgs e) 
{ 
    throw new NotImplementedException(); 
} 

public void SomeMethod1() 
{ 
    BackgroundWorker worker = new BackgroundWorker(); 
    BackgroundWorker backgroundWorker = worker; 
    backgroundWorker.DoWork += (object sender, DoWorkEventArgs e) => throw new NotImplementedException(); 
    worker.RunWorkerAsync(); 
} 

public void SomeMethod2() 
{ 
    BackgroundWorker worker = new BackgroundWorker(); 
    worker.DoWork += worker_DoWork; 
    worker.RunWorkerAsync(); 
} 

private void worker_DoWork(object sender, DoWorkEventArgs e) 
{ 
    throw new NotImplementedException(); 
} 

Edit:

guardare il codice IL c'è solo piccolo overhead nella creazione/assegnazione metodo per delegare per la prima volta.