Подтвердить что ты не робот

+ = new EventHandler (метод) vs + = Метод

Возможный дубликат:
С#: Разница между '+ = anEvent и' ​​+ = new EventHandler (anEvent)

Существует два основных способа подписки на событие:

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

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

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

4b9b3361

Ответ 1

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

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

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% при использовании сохраненного делегирования или создании нового.

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

Таким образом, вывод состоит в том, что запись SomeEvent += new EventHandler(NamedMethod) компилируется в то же самое, что только SomeEvent += NamedMethod. Но если вы планируете позже удалить этот обработчик событий, вам действительно нужно сохранить делегат. Несмотря на то, что класс Delegate имеет код специального кода, который позволяет удалить референциально другого делегата из того, который вы добавили, он должен выполнить нетривиальное количество работы, чтобы снять это.

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

Ответ 2

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

Re: Your Edit

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

Ответ 3

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

Ответ 4

Нет никакой разницы. До .NET 2.0 все назначения переменных должны быть точного типа, компиляторы тогда не делали многого. Чтобы обойти работу, VS 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 и далее, что компилятор С# начал выводить как можно больше.

В этой статье 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

Ответ 5

Нет разницы, первый из них более определен в определении.