Usar expresiones lambda para controladores de eventos

114

Actualmente tengo una página que se declara de la siguiente manera:

public partial class MyPage : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        //snip
        MyButton.Click += (o, i) =>
        {
            //snip
        }
    }
}

Recientemente me mudé a .NET 3.5 desde 1.1, por lo que estoy acostumbrado a escribir controladores de eventos fuera de Page_Load. Mi pregunta es; ¿Hay algún inconveniente de rendimiento o trampas que deba tener en cuenta al usar el método lambda para esto? Lo prefiero, ya que ciertamente es más conciso, pero no quiero sacrificar el rendimiento para usarlo. Gracias.

Cristóbal García
fuente

Respuestas:

117

No hay implicaciones de rendimiento ya que el compilador traducirá su expresión lambda en un delegado equivalente. Las expresiones lambda no son más que una característica del lenguaje que el compilador traduce exactamente al mismo código con el que está acostumbrado a trabajar.

El compilador convertirá el código que tienes a algo como esto:

public partial class MyPage : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        //snip
        MyButton.Click += new EventHandler(delegate (Object o, EventArgs a) 
        {
            //snip
        });
    }
}
Andrew Hare
fuente
Veo. Entonces, ¿tampoco hay inconveniente de tener estos controladores dentro de Page_Load en comparación con tenerlos fuera de él?
Christopher García
1
La convención predominante es adjuntar controladores de eventos en el OnInitmétodo, pero dado que el Clickevento de un botón se generará después de que se cargue la página, este ejemplo está bien.
Andrew Hare
8
Es importante tener en cuenta que sin retener una referencia al delegado, no puede darse de baja del evento.
snarf
3
"el mismo código exacto" es un poco engañoso; al menos cuando se hace referencia a variables locales del método adjunto, las expresiones lambda no se traducen en métodos y algo así como un objeto de cierre que almacena los valores actuales de las variables locales.
OR Mapper
66

En cuanto al rendimiento, es lo mismo que un método con nombre. El gran problema es cuando haces lo siguiente:

MyButton.Click -= (o, i) => 
{ 
    //snip 
} 

Probablemente intentará eliminar una lambda diferente, dejando la original allí. Entonces, la lección es que está bien a menos que también desee poder eliminar el controlador.

Gabe
fuente
3
"¿ Probablemente intentará ..."? ¿ Alguna vez eliminará al manejador correcto en tal situación?
OR Mapper
1
@ORMapper: si la lambda captura una variable, no puede eliminar el controlador correcto. En otras circunstancias, depende del compilador.
Gabe
De Verdad? Interesante, entonces, si registro dos funciones anónimas que se ven iguales (wlog tiene un cuerpo vacío), y luego cancelo el registro (usando -=) otra función anónima que también tiene un cuerpo vacío, es esencialmente indefinido cuál de los dos controladores de eventos eliminarse, o si alguno de ellos se eliminará en absoluto?
OR Mapper
4
@ORMapper: Sí. El compilador puede (pero no tiene que hacerlo) hacer delegados iguales si tienen semántica idéntica (el código no tiene que ser el mismo, pero deben hacer lo mismo) y capturar las mismas instancias de variable (no solo el mismas variables, pero las mismas instancias de esas variables). Consulte la sección 7.10.8 (Operadores de igualdad delegados) de la especificación C # para obtener todos los detalles.
Gabe
12
Si realmente desea usar la lambda pero necesita eliminar el evento, siempre puede mantener el objeto en una variable / campo local y luego eliminarlo, por ejemplovar event = (o, e) => doSomething(); handler += event; doSomethingElse(); handler -= event;
Wai Ha Lee
44
EventHandler handler = (s, e) => MessageBox.Show("Woho");

button.Click += handler;
button.Click -= handler;
usama wahab khan
fuente
1
Información muy útil, aunque fuera de tema (la pregunta es sobre rendimiento).
Stéphane Gourichon
4
No es exactamente fuera de tema, ya que el uso de la memoria puede provocar una degradación del rendimiento.
Vladius
3
Quitarse a sí mismo en un controlador también puede ser útil:c# EventHandler handler = null; handler = (s, e) => { MessageBox.Show("Woho"); button.Click -= handler;}
Vladius
2

No hay implicaciones de rendimiento de las que tenga conocimiento o que alguna vez me haya encontrado, por lo que sé, es solo "azúcar sintáctico" y se compila de la misma manera que el uso de sintaxis delegada, etc.

HEISENBERG
fuente