可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Is it possible to somehow shorten this statement?
if (obj != null)
obj.SomeMethod();
because I happen to write this a lot and it gets pretty annoying. The only thing I can think of is to implement Null Object pattern, but that's not what I can do every time and it's certainly not a solution to shorten syntax.
And similar problem with events, where
public event Func<string> MyEvent;
and then invoke
if (MyEvent != null)
MyEvent.Invoke();
回答1:
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
回答2:
What you're looking for is the Null Conditional (not "coalescing") operator: ?.
. It's available as of C# 6.
Your example would be obj?.SomeMethod();
. If obj is null, nothing happens. When the method has arguments, e.g. obj?.SomeMethod(new Foo(), GetBar());
the arguments are not evaluated if obj
is null, which matters if evaluating the arguments would have side effects.
And chaining is possible: myObject?.Items?[0]?.DoSomething()
回答3:
A quick extension method:
public static void IfNotNull<T>(this T obj, Action<T> action, Action actionIfNull = null) where T : class {
if(obj != null) {
action(obj);
} else if ( actionIfNull != null ) {
actionIfNull();
}
}
example:
string str = null;
str.IfNotNull(s => Console.Write(s.Length));
str.IfNotNull(s => Console.Write(s.Length), () => Console.Write("null"));
or alternatively:
public static TR IfNotNull<T, TR>(this T obj, Func<T, TR> func, Func<TR> ifNull = null) where T : class {
return obj != null ? func(obj) : (ifNull != null ? ifNull() : default(TR));
}
example:
string str = null;
Console.Write(str.IfNotNull(s => s.Length.ToString());
Console.Write(str.IfNotNull(s => s.Length.ToString(), () => "null"));
回答4:
Events can be initialized with an empty default delegate which is never removed:
public event EventHandler MyEvent = delegate { };
No null-checking necessary.
[Update, thanks to Bevan for pointing this out]
Be aware of the possible performance impact, though. A quick micro benchmark I did indicates that handling an event with no subscribers is 2-3 times slower when using the the "default delegate" pattern. (On my dual core 2.5GHz laptop that means 279ms : 785ms for raising 50 million not-subscribed events.). For application hot spots, that might be an issue to consider.
回答5:
Yes, in C# 6.0 -- https://msdn.microsoft.com/en-us/magazine/dn802602.aspx.
object?.SomeMethod()
回答6:
This article by Ian Griffiths gives two different solutions to the problem that he concludes are neat tricks that you should not use.
回答7:
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'.
回答8:
I agree with the answer by Kenny Eliasson. Go with Extension methods. Here is a brief overview of extension methods and your required IfNotNull method.
Extension Methods ( IfNotNull method )
回答9:
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;
}
回答10:
There is a little-known null operator in C# for this, ??. May be helpful:
http://weblogs.asp.net/scottgu/archive/2007/09/20/the-new-c-null-coalescing-operator-and-using-it-with-linq.aspx