Как стать автором
Обновить

Комментарии 27

Да, можно — это решит NullReferenceException, но:
1. Это не очень красиво.
2. Используем не самый последний список подписчиков.
1. Это не очень красиво.

Почему? По-моему красивее, чем описанные в статье варианты, в т.ч. c новым оператором.
2. Используем не самый последний список подписчиков.

А разве вариант с?.. решает эту проблему?
Насчет красоты — конечно, субъективно, но по задумке команды разработчиков C# новый оператор как раз для того случая. Да и для всех случаев, когда можно убрать if (smth != null).

Нет,?.. — только NRE, а вот Volatile.Read / Interlocked.CompareExchange старается решить.
НЛО прилетело и опубликовало эту надпись здесь
Спасибо за комментарий! Да, как показывает MSIL, присвоение и проверка на null — это очень быстро.
Полагаю, для подавляющего большинства проектов на C# такая потеря производительности не стоит внимания. Но вообще да, надо понимать, что пустой делегат не бесплатен.
> Минус данного подхода в том, что метод расширения придется писать для каждого типа обработчика

а дженерики нам на что даны?
public static void Raise<T>(this EventHandler<T> evt, object sender, T args) where T : EventArgs
Да, тоже подумал, когда читал, почему-то Джон не включил это в статью.
Но и?.. все равно легче, потому что никакие дополнительные методы не нужны.
Не все события используют EventHandler
Да, точно, некоторые старые обработчики до .NET 2 используют свои классы.
если нужен лишь факт срабатывания события без каких-либо параметров, можно написать
public event Action Foo = delegate {}

и рейзить его очень просто, без метода-обёртки
Foo();
НЛО прилетело и опубликовало эту надпись здесь
А как же точка под вопросительным знаком? ?.
НЛО прилетело и опубликовало эту надпись здесь
Да, в последних релизах C# старается брать что-то от динамических и функциональных языков, и я считаю, что это здорово. Например, string interpolation в C# 6 хорош (взятый из Ruby).
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
Мне не понятно только одно, почему все постоянно так озадачены тем, что Event?.Invoke использует не наисвежайшую версию делегата?
И да не один из вышеописанных способов не гарантирует получение наисвежайшей версии, между вызовами Volatile.Read и Interlocked.CompareExchange и непосредственно вызовом делегата, оригинал может быть изменен ровно с такой же вероятностью как и без них.
Если нужна гарантия вызова актульной версии, поможет только блокировка.
Мне кажется, в 99.9% случаев достаточно просто вызвать текущий список и никакой проблемы не будет, чем добавлять локи и получать проблемы в производительности. Но представлять как все работает — полезно для разработчика.
Знать полезно это точно, но не раз натыкался, чуть ли не на панику, что вызовется делегат который уже успели отписать!
И каждый раз когда поднимается проблема вызова делегата события, пытаются решить эту проблему странными не эффективными методами.
Но я не понимаю зачем?
НЛО прилетело и опубликовало эту надпись здесь
А откуда пошло название «Элвис-оператор»?
Я вот что-то не могу найти сходства с Элвисом
Имхо вся эта возня как верно заметили решается пустым делегатом с ничтожной потерей производительности. Намного больше проблем может возникнуть, если мы сохранили список с делегатами, а пока мы их вызываем, делегат из конца списка отписался и освободил ресурсы.
using System;
using System.Threading;

namespace ConsoleApplication25
{
    class Program
    {
        static void Main(string[] args)
        {
            var example = new Example();
            for (int i = 0; i < 10; i++)
            {
                int j = i;
                example.Foo += (sender, eventArgs) =>
                               {
                                   Thread.Sleep(100);
                                   Console.WriteLine(j);
                               };
            }
            using (var disposable = new MyDisposable())
            {

                example.Foo += (sender, eventArgs) => disposable.Foo();
                new Thread(() => example.OnFoo()).Start();
            }
            Console.WriteLine("Waiting...");
            Console.ReadKey();
        }
    }

    class Example
    {
        public event EventHandler Foo;

        public void OnFoo()
        {
            EventHandler handler = Foo;
            if (handler != null)
            {
                handler(this, EventArgs.Empty);
            }
        } 
    }

    class MyDisposable : IDisposable
    {
        private bool _disposed;

        public void Dispose()
        {
            _disposed = true;
        }

        public void Foo()
        {
            if (_disposed)
                throw new ObjectDisposedException("MyDisposable");
            Console.WriteLine("Hello world!");
        }
    }
}

Никакой C# 6.0 тут нас не спасет (равно как и пустой делегат, к слову), так что нужно просто смириться с тем, что вызывающему коду нужно быть очень аккуратным в работе с подпиской/отпиской.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории