+= новый EventHandler (метод) vs += метод [дубликат]




Возможные Дубликаты:
C#: разница между '+ = anEvent ' и ' + = new EventHandler (anEvent)’






есть два основных способа подписаться на событие:



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


какая разница, и когда я должен выбрать один над другим?



Edit: если это то же самое, то почему VS по умолчанию использует длинную версию, загромождая код? Для меня это вообще не имеет смысла.

491   5  

5 ответов:

поскольку, казалось, был некоторый спор по поводу моего первоначального ответа, я решил сделать несколько тестов, в том числе глядя на сгенерированный код и мониторинг производительности.

прежде всего, вот наш тестовый стенд, класс с делегатом и другой класс, чтобы использовать его:

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

Первое, что нужно сделать, это посмотреть на сгенерированный 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 
}

так что получается, что да, они генерируют идентичные IL. Я изначально ошибался. Но это не все. Возможно, я ухожу от темы здесь, но я думаю, что важно включить это, когда речь идет о событиях и делегатах:

создание и сравнение различных делегатов не дешево.

когда я писал это, я думал, что первый синтаксис смог привести группу методов в качестве делегата, но оказалось, что это просто преобразование. Но это совершенно другое, когда вы на самом деле сохранить делегат. Если мы добавим это к потребителю:

class Counter
{
    EventHandler savedEvent;

    public Counter()
    {
        savedEvent = CountEvent;
    }

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

вы можете видеть, что это очень различные характеристики, с точки зрения производительности, от двух других:

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

результаты последовательно возвращаются как нечто подобное:

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

на 20% разница при использовании сохраненного делегата против создания нового.

теперь очевидно, что не каждая программа будет добавлять и удаление этого количества делегатов за такое небольшое количество времени, но если вы пишете библиотечные классы-классы, которые могут использоваться способами, которые вы не можете предсказать, - тогда вы действительно хотите сохранить эту разницу в виду, если вам когда-нибудь понадобится добавить и удалить событий (и я написал много кода, который делает это, лично).

Итак, вывод это писать!--5--> компилируется в то же самое, что просто SomeEvent += NamedMethod. Но если вы планируете удалить это событие обработчик позже,вы действительно должны сохранить делегат. Хотя Delegate класс имеет некоторый специальный код, который позволяет удалить ссылочно-другой делегат от того, который вы добавили, он должен выполнить нетривиальный объем работы, чтобы снять это.

если вы не собираетесь сохранять делегат, то это не имеет никакого значения - компилятор в конечном итоге создает новый делегат в любом случае.

нет никакой разницы с точки зрения программирования, они эквивалентны друг другу. Компилятор будет делать то, что вы сделали на первой линии со второй линии за кулисами. Поэтому я всегда выбирал бы второй подход (меньше кода).

Re: Ваше Редактирование

наверное, потому что они чувствуют себя лучше, чтобы показать разработчикам правильный способ делать вещи, а не ярлыки. Ваша догадка так же хороша, как и моя :)

вторая форма-синтаксический сахар, введенный в более поздних версиях c#. первая строка будет работать в каждой версии, хотя

нет никакой разницы. До .NET 2.0 все назначения переменных должны иметь точный тип, компиляторы тогда не выводили много. Так как, чтобы сделать работу-вокруг, против 2003 испускают new EventHandler вокруг имени функции. Это только мое предположение. Потому что..

Я пробовал что-то сейчас в VS 2008, textBox1.KeyDown += (KeyEventHandler)textBox1_KeyDown, это тоже работает. Это озадачивает меня, почему они выбирают new EventHandler(checkBox1_CheckStateChanged), а не (EventHandler)checkBox1_CheckStateChanged затем. Но...

поскольку у меня больше нет VS 2003 в моей коробке, я не могу определить, если кастинг подход может также работать на VS 2003. Но afaict, я попытался удалить new EventHandler на имя функции, когда я использовал VS 2003 (.NET 1.1), считая, почему необходимо создать экземпляр (new EventHandler) функция, делегаты просто указатель функции под капотом, но это не работает.

только с .NET 2.0 и далее компилятор C# начал выводить как можно больше.

эта статья http://blueonionsoftware.com/blog.aspx?p=aed2ae46-7548-4e5f-83c6-95e00c6f3649 поддерживается моя память о new EventHandler до компиляторов .NET 2.0 это был принудительный

[EDIT]

в следующей статье подробно описывается подписка / неподписание событий, предполагая, что есть разница между button1.Click += new EventHandler(button1_Click); и button1.Click += button1_Click;, но, к сожалению, я не вижу никакой разницы в уровне IL, хотя: - (

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

нет никакой разницы, первый просто более конкретен в своем определении.

Comments

    Ничего не найдено.