Вы должны всегда проверять, нет ли у делегата целей (его значение равно нулю) перед его отправкой.
Как было сказано ранее, один из способов сделать это - подписаться на анонимный метод, который ничего не делает, который не будет удален.
public event MyDelegate Fire = delegate {};
Однако это всего лишь взлом, чтобы избежать исключений NullReferenceExceptions.
Просто проверка, является ли делегат нулевым перед вызовом, не является потокобезопасной, поскольку другой поток может отменить регистрацию после нулевой проверки и сделать его нулевым при вызове.
Есть и другое решение - скопировать делегат во временную переменную:
public event MyDelegate Fire;
public void FireEvent(string msg)
{
MyDelegate temp = Fire;
if (temp != null)
temp(msg);
}
К сожалению, JIT-компилятор может оптимизировать код, исключить временную переменную и использовать исходный делегат. (согласно Juval Lowy - Программирование компонентов .NET)
Таким образом, чтобы избежать этой проблемы, вы можете использовать метод, который принимает делегат в качестве параметра:
[MethodImpl(MethodImplOptions.NoInlining)]
public void FireEvent(MyDelegate fire, string msg)
{
if (fire != null)
fire(msg);
}
Обратите внимание, что без атрибута MethodImpl (NoInlining) JIT-компилятор может встроить метод, делая его бесполезным.
Поскольку делегаты являются неизменяемыми, эта реализация является поточно-ориентированной.
Вы можете использовать этот метод как:
FireEvent(Fire,"Hello 3");