+ = new EventHandler (metoda) vs += metoda [duplikat]

Możliwy duplikat:
C#: różnica między '+ = anEvent ' i ' + = new EventHandler (anEvent)'

Istnieją dwa podstawowe sposoby zapisania się na wydarzenie:

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

Jaka jest różnica i kiedy powinienem wybrać jedno nad drugim?

Edit: jeśli jest to to samo, to dlaczego VS domyślnie do długiej wersji, zaśmiecając kod? To nie ma dla mnie żadnego sensu.

 68
Author: Community, 2010-05-01

5 answers

Ponieważ wydawało się, że jest jakiś spór co do mojej pierwotnej odpowiedzi, postanowiłem zrobić kilka testów, w tym patrząc na wygenerowany kod i monitorowanie wydajności.

Po pierwsze, oto nasze łóżko testowe, klasa z delegatem i inna klasa, która go spożywa: {]}

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++;
    }
}

Pierwszą rzeczą, którą należy zrobić, to spojrzeć na wygenerowany IL:

.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 
}

Okazuje się więc, że tak, te generują identyczne IL. Początkowo się myliłem. Ale to jest nie cały historia . Może być tak, że wychodzę tutaj poza temat, ale myślę, że ważne jest, aby uwzględnić to podczas mówienia o wydarzeniach i delegatach: {]}

Tworzenie i porównywanie różnych delegatów nie jest tanie.

Kiedy to pisałem, myślałem, że pierwsza składnia była w stanie obsadzić grupę metod jako delegata, ale okazało się, że to tylko konwersja. Ale jest zupełnie inaczej, kiedy faktycznie zapisać delegata. Jeśli dodamy to do konsument:

class Counter
{
    EventHandler savedEvent;

    public Counter()
    {
        savedEvent = CountEvent;
    }

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

Widać, że to ma bardzo inne cechy, pod względem wydajności, od dwóch pozostałych:

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;
}

Wyniki konsekwentnie wracają jako coś podobnego do:

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

To prawie 20% różnica podczas używania zapisanego delegata w porównaniu z tworzeniem nowego delegata.

Teraz oczywiście nie każdy program będzie dodawał i usuwał tak wielu delegatów w tak krótkim czasie, ale jeśli piszesz klasy biblioteczne - klasy, które mogą być używane w sposób, którego nie można przewidzieć - wtedy naprawdę chcesz zachować tę różnicę w umyśle, jeśli kiedykolwiek będziesz musiał dodać i usunąć zdarzenia(i napisałem dużo kodu, który to robi, osobiście).

Więc wniosek z tego jest taki, że pisanie SomeEvent += new EventHandler(NamedMethod) kompiluje się do tego samego, co tylko SomeEvent += NamedMethod. Ale jeśli planujesz usunąć obsługę tego zdarzenia później, naprawdę powinieneś zapisać delegata . Mimo, że klasa Delegate ma jakiś szczególny przypadek kod, który pozwala na usunięcie referencyjnie-innego delegata od tego, który dodałeś, musi wykonać nietrywialną ilość pracy, aby to zrobić.

Jeśli nie zamierzasz zapisać delegata, to nie ma to znaczenia - kompilator i tak tworzy nowy delegat.

 38
Author: Aaronaught,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-01 15:47:15

Nie ma różnicy z punktu widzenia programowania, są one wzajemnie równoważne. Kompilator zrobi prawie to, co zrobiłeś w pierwszej linii z drugą linią za kulisami. Dlatego zawsze wybierałbym drugie podejście(mniej kodu).

Re: Twoja Edycja

Prawdopodobnie dlatego, że czują się lepiej, aby pokazać programistom właściwe sposób robienia rzeczy, a nie skróty. Twoje przypuszczenie jest tak samo dobre jak Moje:)

 25
Author: James,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-01 12:29:00

Drugą formą jest cukier składniowy wprowadzony w późniejszych wersjach c#. pierwsza linia będzie działać w każdej wersji

 9
Author: knittl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-01 12:57:26

Nie ma różnicy. Przed. NET 2.0, każde przypisanie zmiennej musi być dokładnie typu, Kompilatory wtedy nie wyciągnął wiele wniosków. Aby zrobić obejście, VS 2003 emituje new EventHandler wokół nazwy funkcji. To tylko moje przypuszczenie. Ponieważ..

Próbowałem czegoś TERAZ W VS 2008, textBox1.KeyDown += (KeyEventHandler)textBox1_KeyDown, to również działa. Zastanawia mnie, dlaczego wybierają new EventHandler(checkBox1_CheckStateChanged), a nie (EventHandler)checkBox1_CheckStateChanged wtedy. Ale...

Ponieważ nie mam już VS 2003 w moim pudełku, nie mogę określić, czy podejście castingowe może również działać na VS 2003. Ale afaict, próbowałem usunąć new EventHandler Na nazwę funkcji, gdy używałem VS 2003 (. NET 1.1), rozważając dlaczego trzeba utworzyć instancję (new EventHandler) funkcji, delegaty są tylko wskaźnikiem funkcji pod maską, ale to nie działa.

Dopiero od. NET 2.0 kompilator C# zaczął wnioskować jak najwięcej.

Ten artykuł http://blueonionsoftware.com/blog.aspx?p=aed2ae46-7548-4e5f-83c6-95e00c6f3649 obsługiwał moją pamięć new EventHandler przed kompilatorami. NET 2.0, był kompulsary

[EDIT]

Poniższy artykuł mówi o subskrybowaniu / wypisywaniu zdarzeń, rzekomo Istnieje różnica między button1.Click += new EventHandler(button1_Click); i button1.Click += button1_Click;, ale niestety nie widzę żadnej różnicy w poziomie IL: - (

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

 7
Author: Michael Buen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-01 13:53:06

Nie ma różnicy, pierwsza jest po prostu bardziej szczegółowa w swojej definicji.

 2
Author: Thomas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-01 12:18:29