ОНОВЛЕННЯ
Відповідно до C # 6, відповідь на це питання:
SomeEvent?.Invoke(this, e);
Я часто чую / читаю такі поради:
Завжди робіть копію події, перш ніж перевірити її null
та запустити її. Це дозволить усунути потенційну проблему з нанизуванням, де подія стає null
в точці розташування між місцем, де ви перевіряєте на нуль, і де ви запускаєте подію:
// Copy the event delegate before checking/calling
EventHandler copy = TheEvent;
if (copy != null)
copy(this, EventArgs.Empty); // Call any handlers on the copied list
Оновлено : Я читав, читаючи про оптимізації, що це також може вимагати змін учасника події, але Джон Скіт у своїй відповіді заявляє, що CLR не оптимізує копію.
Тим часом, для того, щоб ця проблема навіть мала місце, інша тема повинна зробити щось подібне:
// Better delist from event - don't want our handler called from now on:
otherObject.TheEvent -= OnTheEvent;
// Good, now we can be certain that OnTheEvent will not run...
Фактичною послідовністю може бути ця суміш:
// Copy the event delegate before checking/calling
EventHandler copy = TheEvent;
// Better delist from event - don't want our handler called from now on:
otherObject.TheEvent -= OnTheEvent;
// Good, now we can be certain that OnTheEvent will not run...
if (copy != null)
copy(this, EventArgs.Empty); // Call any handlers on the copied list
Справа в тому, що OnTheEvent
працює після того, як автор скасував підписку, і все ж вони просто скасували передплату, щоб уникнути цього. Безумовно, що дійсно потрібно - це власна реалізація події з відповідною синхронізацією в add
та remove
аксесуарах. І крім того, існує проблема можливих тупиків, якщо блокування проводиться під час запуску події.
Так це і є програмування вантажних культів ? Мабуть, так - багато людей повинні робити цей крок, щоб захистити свій код від декількох потоків, коли насправді мені здається, що події вимагають набагато більше уваги, ніж це, перш ніж їх можна використовувати як частину багатопотокової конструкції . Отже, люди, які не сприймають такої додаткової обережності, можуть також ігнорувати цю пораду - це просто не є проблемою для однопотокових програм, а насправді, зважаючи на відсутність volatile
у більшості прикладних кодів в Інтернеті, поради можуть бути відсутніми ефект взагалі.
(І чи не набагато простіше просто призначити порожнє delegate { }
в декларації члена, щоб ніколи не потрібно було перевірятиnull
це?)
Оновлено:Якщо це не було зрозуміло, я зрозумів намір поради - уникати нульового виключення за будь-яких обставин. Моя думка полягає в тому, що цей конкретний нульовий посилання на виняток може виникнути лише в тому випадку, якщо інший потік відхиляється від події, і єдиною причиною цього є забезпечення того, щоб додаткові дзвінки не надходили через цю подію, що очевидно НЕ досягається цією технікою . Ви б приховували умову гонки - краще було б розкрити її! Цей нульовий виняток допомагає виявити зловживання вашим компонентом. Якщо ви хочете, щоб ваш компонент був захищений від зловживань, ви можете наслідувати приклад WPF - зберігайте ідентифікатор потоку у своєму конструкторі, а потім кидайте виняток, якщо інший потік намагається взаємодіяти безпосередньо з вашим компонентом. Або ж реалізуйте справді безпечний для компонентів компонент (завдання не з легких).
Тому я стверджую, що просто виконувати цю ідіому копіювання / перевірки - це культове програмування вантажу, додаючи безлад та шум у ваш код. Щоб реально захиститись від інших ниток, потрібно набагато більше роботи.
Оновлення у відповідь на повідомлення в блозі Еріка Ліпперта:
Тож є головне, що я пропустив у обробниках подій: "Обробники подій повинні бути надійними, якщо вони можуть бути викликані навіть після відмови від підписки", і, очевидно, тому нам потрібно дбати лише про можливість події. делегат буття null
. Чи є ця вимога до обробників подій документована де-небудь?
І так: "Є й інші способи вирішити цю проблему; наприклад, ініціалізація обробника на порожню дію, яка ніколи не видаляється. Але проведення нульової перевірки є стандартною схемою".
Отже, один з фрагментів мого запитання полягає в тому , чому явна перевірка нуля перевіряє "стандартний зразок"? Альтернатива, призначаючи порожній делегат, вимагає лише= delegate {}
додавати до декларації події, і це виключає ті маленькі купи смердючої церемонії з усіх місць, де відбулася подія. Було б легко переконатися, що порожній делегат дешевий для отримання екземпляра. Або я все-таки щось пропускаю?
Безумовно, що так (як запропонував Джон Скіт), це просто .NET 1.x порада, яка не вимерла, як це слід було зробити в 2005 році?
EventName(arguments)
беззастережно викликати делегата події, а не викликати цього лише делегата, якщо він не має значення (не робити нічого, якщо нуль).