2012-02-22 20 views
5

Me gustaría saber si se incurre en gastos generales mediante el uso de métodos anónimos al crear un trabajador en segundo plano.¿Hay alguna sobrecarga en el uso de métodos anónimos?

por ejemplo:

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

    worker.RunWorkerAsync(); 
} 

¿Sería el ejemplo anterior ser mejor o peor que la definición de la //large amount of code en un método separado?

¿Se incurre en gastos generales al definir el método de trabajador en segundo plano en línea, especialmente si se llama a menudo SomeMethod()?

+0

Si hay una gran cantidad de código que probablemente desee refaccionarlo en una pequeña cantidad de código llamando a otros métodos ... – Chris

Respuesta

4

Hay una pequeña diferencia en cómo se manejan los métodos nombrados y los métodos anónimos cuando se crea un delegado de ellos.

Los delegados para métodos anónimos se almacenan en caché, por lo que hay una pequeña sobrecarga para comprobar si el delegado ya existe en la memoria caché. Por otro lado, si ejecuta el método más de una vez, reutilizará el delegado almacenado en caché en lugar de crear uno nuevo.

Los delegados para métodos con nombre no se almacenan en la memoria caché, por lo que se crearán cada vez.

Aparte de eso, no hay diferencia. El método anónimo se creará en tiempo de compilación y existe en el código como un método normal, solo con un nombre que solo el compilador conoce.

1

Cuando un método anónimo (incluido lambdas) se cierra sobre variables, el compilador crea una clase para contener estas variables. Siempre que se crea el delegado, una nueva instancia de esta clase también lo es. Esto obviamente agrega trabajo adicional para el tiempo de ejecución, pero generalmente es insignificante en la mayoría de las situaciones.

+0

Supongo que si no hay cierre no creará la clase por lo que no hará ninguna diferencia? – Chris

+0

@Chris Sí, el compilador es lo suficientemente inteligente como para crear un tipo anónimo (para mantener el método anónimo más variables cerradas) solo cuando sea necesario. –

+0

La clase de cierre no se crea cuando crea el delegado. Se crea antes de usar el local cerrado, porque el acceso a los locales en el método "principal" tiene que usar realmente la clase de cierre. – svick

0

Estaba probando esto el otro día (mediante el uso de la clase StopWatch). Por lo que pude ver no había diferencia notable en funcionamiento entre la invocación de un método directamente ...

SomeMethod(); 

... o por medio de un método anónimo ...

() => SomeMethod(); 
+2

El compilador optimizará la necesidad de crear una clase anónima para mantener el método anónimo ya que no se cierran las variables. –

+0

Interesante, pensé que el compilador podría estar involucrado en eso. Dos preguntas: si el cuerpo del método anónimo hace referencia a variables fuera de su alcance, ¿eso introducirá un gasto general? Y, de ser así, ¿cuándo se encontraría esa sobrecarga? Durante la creación o ejecución del método anónimo? – Moonshield

2

Primero, probablemente no deberías poner una gran cantidad de código en un método anónimo. Sería más legible si creas un método separado para eso, o mejor aún, varios métodos.

En cuanto a la IL generada, si la lambda no cierra sobre ninguna variable, entonces el código IL generado es el mismo que si pones el código en el método normal (excepto que el método generado tiene un nombre indecible) .

Por otro lado, si cierra sobre alguna variable, el compilador crea una clase de cierre para mantener esa variable en un campo. Y el acceso al campo es un poco más caro que el acceso variable local.

En resumen, si cierra algunas variables, hay una sobrecarga pequeña (incluidos más objetos que se deben recolectar como basura). En la mayoría de las situaciones, esto no importa y preocuparse por esto sería una optimización prematura. Pero si crees que sí importa, debes perfilar el código.

0

Esto es lo que decompilador dijo:

[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(); 
} 

Editar:

mirar el código IL sólo hay pequeña sobrecarga en la creación/asignación método para delegar por primera vez.

Cuestiones relacionadas