При обновлении SonarQube вы должны подготовиться к некоторым (большим) изменениям. Между 4.5.4 и 6.7.5 существует большой разрыв, поэтому не удивляйтесь, что шашки были улучшены и серьезность пересмотрена.
Это нормально, и вам следует проанализировать изменения перед выполнением любого обновления приложения.
From C# 6 onwards, you can just use:
MyEvent?.Invoke();
or:
obj?.SomeMethod();
The ?.
is the null-propagating operator, and will cause the .Invoke()
to be short-circuited when the operand is null
. The operand is only accessed once, so there is no risk of the "value changes between check and invoke" problem.
===
Prior to C# 6, no: there is no null-safe magic, with one exception; extension methods - for example:
public static void SafeInvoke(this Action action) {
if(action != null) action();
}
now this is valid:
Action act = null;
act.SafeInvoke(); // does nothing
act = delegate {Console.WriteLine("hi");}
act.SafeInvoke(); // writes "hi"
In the case of events, this has the advantage of also removing the race-condition, i.e. you don't need a temporary variable. So normally you'd need:
var handler = SomeEvent;
if(handler != null) handler(this, EventArgs.Empty);
but with:
public static void SafeInvoke(this EventHandler handler, object sender) {
if(handler != null) handler(sender, EventArgs.Empty);
}
we can use simply:
SomeEvent.SafeInvoke(this); // no race condition, no null risk
События могут быть инициализированы с помощью пустого делегата по умолчанию, который никогда не удаляется:
public event EventHandler MyEvent = delegate { };
Нет необходимости в проверке нуля.
[ Обновление , спасибо Бевану за указание на это]
Однако имейте в виду возможное влияние на производительность. Проведенный мною небольшой тест показал, что обработка события без подписчиков происходит в 2-3 раза медленнее при использовании шаблона «делегат по умолчанию». (На моем двухъядерном ноутбуке с тактовой частотой 2,5 ГГц это означает 279 мс: 785 мс для сбора 50 миллионов событий без подписки.) Это может быть проблемой для горячих точек приложений.
This article by Ian Griffiths gives two different solutions to the problem that he concludes are neat tricks that you should not use.
В C # для этого есть малоизвестный нулевой оператор ??. Может быть полезно:
Cerating extention method like one suggested does not really solve issues with race conditions, but rather hide them.
public static void SafeInvoke(this EventHandler handler, object sender)
{
if (handler != null) handler(sender, EventArgs.Empty);
}
As stated this code is the elegant equivalent to solution with temporary variable, but...
The problem with both that it's possible that subsciber of the event could be called AFTER it has unsubscribed from the event. This is possible because unsubscription can happen after delegate instance is copied to the temp variable (or passed as parameter in the method above), but before delegate is invoked.
In general the behaviour of the client code is unpredictable in such case: component state could not allow to handle event notification already. It's possible to write client code in the way to handle it, but it would put unnecesssary responsibility to the client.
The only known way to ensure thread safity is to use lock statement for the sender of the event. This ensures that all subscriptions\unsubscriptions\invocation are serialized.
To be more accurate lock should be applied to the same sync object used in add\remove event accessor methods which is be default 'this'.
Maybe not better but in my opinion more readable is to create an extension method
public static bool IsNull(this object obj) {
return obj == null;
}