una que net manejo lista librería invocar eventos evento event controlar biblioteca c# events

que - public event c#



+=new EventHandler(método) vs+= método (5)

Como parecía haber alguna disputa sobre mi respuesta original, decidí hacer algunas pruebas, incluso mirando el código generado y monitoreando el rendimiento.

Antes que nada, aquí está nuestro banco de pruebas, una clase con un delegado y otra clase para consumirla:

class EventProducer { public void Raise() { var handler = EventRaised; if (handler != null) handler(this, EventArgs.Empty); } public event EventHandler EventRaised; } class Counter { long count = 0; EventProducer producer = new EventProducer(); public void Count() { producer.EventRaised += CountEvent; producer.Raise(); producer.EventRaised -= CountEvent; } public void CountWithNew() { producer.EventRaised += new EventHandler(CountEvent); producer.Raise(); producer.EventRaised -= new EventHandler(CountEvent); } private void CountEvent(object sender, EventArgs e) { count++; } }

Lo primero que debe hacer es observar el IL generado:

.method public hidebysig instance void Count() cil managed { .maxstack 8 L_0000: ldarg.0 L_0001: ldfld class DelegateTest.Program/EventProducer DelegateTest.Program/Counter::producer L_0006: ldarg.0 L_0007: ldftn instance void DelegateTest.Program/Counter::CountEvent(object, class [mscorlib]System.EventArgs) L_000d: newobj instance void [mscorlib]System.EventHandler::.ctor(object, native int) L_0012: callvirt instance void DelegateTest.Program/EventProducer::add_EventRaised(class [mscorlib]System.EventHandler) L_0017: ldarg.0 L_0018: ldfld class DelegateTest.Program/EventProducer DelegateTest.Program/Counter::producer L_001d: callvirt instance void DelegateTest.Program/EventProducer::Raise() L_0022: ldarg.0 L_0023: ldfld class DelegateTest.Program/EventProducer DelegateTest.Program/Counter::producer L_0028: ldarg.0 L_0029: ldftn instance void DelegateTest.Program/Counter::CountEvent(object, class [mscorlib]System.EventArgs) L_002f: newobj instance void [mscorlib]System.EventHandler::.ctor(object, native int) L_0034: callvirt instance void DelegateTest.Program/EventProducer::remove_EventRaised(class [mscorlib]System.EventHandler) L_0039: ret } .method public hidebysig instance void CountWithNew() cil managed { .maxstack 8 L_0000: ldarg.0 L_0001: ldfld class DelegateTest.Program/EventProducer DelegateTest.Program/Counter::producer L_0006: ldarg.0 L_0007: ldftn instance void DelegateTest.Program/Counter::CountEvent(object, class [mscorlib]System.EventArgs) L_000d: newobj instance void [mscorlib]System.EventHandler::.ctor(object, native int) L_0012: callvirt instance void DelegateTest.Program/EventProducer::add_EventRaised(class [mscorlib]System.EventHandler) L_0017: ldarg.0 L_0018: ldfld class DelegateTest.Program/EventProducer DelegateTest.Program/Counter::producer L_001d: callvirt instance void DelegateTest.Program/EventProducer::Raise() L_0022: ldarg.0 L_0023: ldfld class DelegateTest.Program/EventProducer DelegateTest.Program/Counter::producer L_0028: ldarg.0 L_0029: ldftn instance void DelegateTest.Program/Counter::CountEvent(object, class [mscorlib]System.EventArgs) L_002f: newobj instance void [mscorlib]System.EventHandler::.ctor(object, native int) L_0034: callvirt instance void DelegateTest.Program/EventProducer::remove_EventRaised(class [mscorlib]System.EventHandler) L_0039: ret }

Entonces resulta que, sí, generan un IL idéntico. Estaba equivocado originalmente. Pero esa no es toda la historia . Puede ser que me estoy saliendo de tema aquí, pero creo que es importante incluir esto cuando se habla de eventos y delegados:

Crear y comparar diferentes delegados no es barato.

Cuando escribí esto, pensé que la primera sintaxis podía convertir al grupo de métodos como delegado, pero resulta que solo es una conversión. Pero es completamente diferente cuando realmente guarda el delegado. Si agregamos esto al consumidor:

class Counter { EventHandler savedEvent; public Counter() { savedEvent = CountEvent; } public void CountSaved() { producer.EventRaised += savedEvent; producer.Raise(); producer.EventRaised -= savedEvent; } }

Puedes ver que esto tiene características muy diferentes, en cuanto a rendimiento, de los otros dos:

static void Main(string[] args) { const int TestIterations = 10000000; TimeSpan countTime = TestCounter(c => c.Count()); Console.WriteLine("Count: {0}", countTime); TimeSpan countWithNewTime = TestCounter(c => c.CountWithNew()); Console.WriteLine("CountWithNew: {0}", countWithNewTime); TimeSpan countSavedTime = TestCounter(c => c.CountSaved()); Console.WriteLine("CountSaved: {0}", countSavedTime); Console.ReadLine(); } static TimeSpan TestCounter(Action<Counter> action, int iterations) { var counter = new Counter(); Stopwatch sw = new Stopwatch(); sw.Start(); for (int i = 0; i < TestIterations; i++) action(counter); sw.Stop(); return sw.Elapsed; }

Los resultados siempre regresan como algo similar a:

Count: 00:00:02.4742007 CountWithNew: 00:00:02.4272702 CountSaved: 00:00:01.9810367

Eso es casi un 20% de diferencia al usar un delegado guardado versus crear uno nuevo.

Ahora, obviamente, no todos los programas agregarán y eliminarán tantos delegados en tan poco tiempo, pero si estás escribiendo clases de biblioteca, clases que podrían usarse de formas que no puedes predecir, entonces realmente quieres mantener esto. la diferencia en mente si alguna vez necesita agregar y eliminar eventos (y he escrito mucho código que hace esto, personalmente).

Por lo tanto, la conclusión de esto es que escribir SomeEvent += new EventHandler(NamedMethod) compila a la misma cosa que SomeEvent += NamedMethod . Pero si planea eliminar ese controlador de eventos más tarde, realmente debería guardar el delegado . A pesar de que la clase Delegate tiene un código de caso especial que le permite eliminar un delegado diferente desde el que usted agregó, tiene que hacer una cantidad de trabajo no trivial para lograrlo.

Si no va a guardar el delegado, entonces no importa: el compilador termina creando un nuevo delegado de todos modos.

Posible duplicado:
C #: Diferencia entre ''+ = anEvent'' y ''+ = new EventHandler (anEvent)''

Hay dos formas básicas de suscribirse a un evento:

SomeEvent += new EventHandler<ArgType> (MyHandlerMethod); SomeEvent += MyHandlerMethod;

¿Cuál es la diferencia, y cuándo debería elegir uno sobre el otro?

Editar: si es lo mismo, ¿por qué VS se predetermina a la versión larga, abarrotando el código? Eso no tiene ningún sentido para mí.


No hay diferencia Antes de .NET 2.0, cada asignación de variables debe ser del tipo exacto, los compiladores no infieren demasiado. Para hacer una reparación, VS 2003 emite un new EventHandler alrededor del nombre de la función. Esa es solo mi suposición. Porque..

Intenté algo ahora en VS 2008, textBox1.KeyDown += (KeyEventHandler)textBox1_KeyDown , que también funciona. Me desconcierta por qué eligen new EventHandler(checkBox1_CheckStateChanged) , en lugar de (EventHandler)checkBox1_CheckStateChanged . Pero...

como ya no tengo VS 2003 en mi caja, no puedo precisar si el enfoque de conversión también podría funcionar en VS 2003. Pero enfajado, intenté eliminar el new EventHandler en el nombre de la función cuando utilicé VS 2003 (.NET 1.1 ), considerando por qué la necesidad de instanciar ( new EventHandler ) una función, los delegados son sólo un puntero de función bajo el capó, pero no funciona.

Solo desde .NET 2.0 en adelante el compilador de C # comenzó a inferir tanto como sea posible.

Este artículo http://blueonionsoftware.com/blog.aspx?p=aed2ae46-7548-4e5f-83c6-95e00c6f3649 admitía mi memoria del new EventHandler anterior a los compiladores de .NET 2.0, era obligatorio

[EDITAR]

El siguiente artículo profundiza sobre los eventos de suscripción / desincorporación, indicando que hay una diferencia entre button1.Click += new EventHandler(button1_Click); y button1.Click += button1_Click; , pero lamentablemente no puedo ver ninguna diferencia en el nivel de IL :-(

http://blogs.msdn.com/abhinaba/archive/2005/08/26/456437.aspx


No hay diferencia desde la perspectiva de la programación, son equivalentes entre sí. El compilador hará prácticamente lo que has hecho en la primera línea con la segunda línea detrás de escena. Así que siempre optaría por el segundo enfoque (menos código).

Re: Tu Edición

Probablemente porque sienten que es mejor mostrarles a los desarrolladores la forma correcta de hacer las cosas en lugar de atajos. Tu invitado es tan bueno como el mío :)


No hay diferencia, el primero solo es más específico en su definición.


la segunda forma es azúcar sintáctica introducida en versiones posteriores de c #. la primera línea funcionará en todas las versiones, aunque