Pull to refresh

Comments 27

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

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

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

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

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

и рейзить его очень просто, без метода-обёртки
Foo();
UFO landed and left these words here
А как же точка под вопросительным знаком? ?.
UFO landed and left these words here
Да, в последних релизах C# старается брать что-то от динамических и функциональных языков, и я считаю, что это здорово. Например, string interpolation в C# 6 хорош (взятый из Ruby).
UFO landed and left these words here
UFO landed and left these words here
UFO landed and left these words here
Мне не понятно только одно, почему все постоянно так озадачены тем, что Event?.Invoke использует не наисвежайшую версию делегата?
И да не один из вышеописанных способов не гарантирует получение наисвежайшей версии, между вызовами Volatile.Read и Interlocked.CompareExchange и непосредственно вызовом делегата, оригинал может быть изменен ровно с такой же вероятностью как и без них.
Если нужна гарантия вызова актульной версии, поможет только блокировка.
Мне кажется, в 99.9% случаев достаточно просто вызвать текущий список и никакой проблемы не будет, чем добавлять локи и получать проблемы в производительности. Но представлять как все работает — полезно для разработчика.
Знать полезно это точно, но не раз натыкался, чуть ли не на панику, что вызовется делегат который уже успели отписать!
И каждый раз когда поднимается проблема вызова делегата события, пытаются решить эту проблему странными не эффективными методами.
Но я не понимаю зачем?
UFO landed and left these words here
А откуда пошло название «Элвис-оператор»?
Я вот что-то не могу найти сходства с Элвисом
Имхо вся эта возня как верно заметили решается пустым делегатом с ничтожной потерей производительности. Намного больше проблем может возникнуть, если мы сохранили список с делегатами, а пока мы их вызываем, делегат из конца списка отписался и освободил ресурсы.
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 тут нас не спасет (равно как и пустой делегат, к слову), так что нужно просто смириться с тем, что вызывающему коду нужно быть очень аккуратным в работе с подпиской/отпиской.
Sign up to leave a comment.

Articles