Dado que parecía haber alguna disputa sobre mi respuesta original, decidí hacer algunas pruebas, incluso mirando el código generado y monitoreando el rendimiento.
En primer lugar, aquí es nuestro banco de pruebas, una clase con un delegado y otra clase de consumirlo:
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++;
}
}
primero que debe hacer es mirar a la IL generada:
.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 eso es no toda la historia. Puede ser que esté fuera 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, estaba pensando 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 sumamos esto al consumidor:
class Counter
{
EventHandler savedEvent;
public Counter()
{
savedEvent = CountEvent;
}
public void CountSaved()
{
producer.EventRaised += savedEvent;
producer.Raise();
producer.EventRaised -= savedEvent;
}
}
se puede ver que esto tiene características muy diferentes, en cuanto al 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 consistentemente 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% diferencia cuando se utiliza una d salvado elegate vs. crear uno nuevo.
Ahora, obviamente, no todos los programas agregarán y eliminarán tantos delegados en tan poco tiempo, pero si está escribiendo clases de biblioteca, clases que podrían usarse de maneras que no puede predecir, entonces realmente Desea mantener esta diferencia en mente si alguna vez necesita agregar y eliminar los eventos (y he escrito una gran cantidad de código que hace esto, personalmente).
Por lo tanto, la conclusión de esto es escribir SomeEvent += new EventHandler(NamedMethod)
compila a la misma cosa que . Pero si planea eliminar ese controlador de eventos más tarde, realmente debería guardar delegado. Aunque la clase Delegate
tiene un código de caso especial que le permite eliminar un delegado diferente al que ha agregado, 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.
Los generadores de código se amontonan con más frecuencia (utilizando nombres calificados largos en lugar de usar directivas). Su objetivo es la facilidad de generación y evitar errores, no la legibilidad. –
Estaba hablando específicamente sobre el fragmento de código, que no es utilizado por un generador de códigos automatizado. – mafu
Yo también. Las formas largas son menos propensas a crear ambigüedades. –