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

О комментарии @ calfzhou , вы можете использовать, как обычно, **kwargs:

Пример работы в режиме онлайн

class A(object):
  def __init__(self, a, *args, **kwargs):
    print("A", a)

class B(A):
  def __init__(self, b, *args, **kwargs):
    super(B, self).__init__(*args, **kwargs)
    print("B", b)

class A1(A):
  def __init__(self, a1, *args, **kwargs):
    super(A1, self).__init__(*args, **kwargs)
    print("A1", a1)

class B1(A1, B):
  def __init__(self, b1, *args, **kwargs):
    super(B1, self).__init__(*args, **kwargs)
    print("B1", b1)


B1(a1=6, b1=5, b="hello", a=None)

Результат:

A None
B hello
A1 6
B1 5

Вы также можете использовать их позиционно:

B1(5, 6, b="hello", a=None)

, но вы должны помнить MRO, это действительно запутанно.

Я могу быть немного раздражающим, но я заметил, что люди каждый раз забыли использовать *args и **kwargs, когда они переопределяют метод, в то время как это одно из немногих действительно полезных и разумных способов использования этих «магических переменных».

72
задан Community 23 May 2017 в 12:34
поделиться

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

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

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

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

42
ответ дан 24 November 2019 в 12:45
поделиться

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

9
ответ дан 24 November 2019 в 12:45
поделиться

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

Re: Your Edit

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

25
ответ дан 24 November 2019 в 12:45
поделиться

Нет никакой разницы. До .NET 2.0 каждое присваивание переменной должно было иметь точный тип, тогда компиляторы ничего не делали. Чтобы обойти эту проблему, VS 2003 генерирует новый обработчик событий вокруг имени функции. Это только мое предположение. Потому что ..

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

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

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

Эта статья http://blueonionsoftware.com/blog.aspx?p=aed2ae46-7548-4e5f-83c6-95e00c6f3649 поддерживает мою память о новом обработчике событий до .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

7
ответ дан 24 November 2019 в 12:45
поделиться

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

2
ответ дан 24 November 2019 в 12:45
поделиться
Другие вопросы по тегам:

Похожие вопросы: