方法调用,如果在C#中不为空
是否有可能以某种方式缩短这个陈述?
if (obj != null) obj.SomeMethod();
因为我碰巧写了很多,而且非常恼人。 我唯一能想到的是实现Null对象模式,但这不是我每次都能做到的,当然也不是缩短语法的解决scheme。
与事件类似的问题,在哪里
public event Func<string> MyEvent;
然后调用
if (MyEvent != null) MyEvent.Invoke();
从C#6开始,您可以使用:
MyEvent?.Invoke();
要么:
obj?.SomeMethod();
那个?.
是空传播运算符,当操作数为null
时,会导致.Invoke()
短路。 操作数只被访问一次,所以没有“检查和调用之间的值变化”问题的风险。
===
在C#6之前,不:没有无效的魔法,只有一个例外; 扩展方法 – 例如:
public static void SafeInvoke(this Action action) { if(action != null) action(); }
现在这是有效的:
Action act = null; act.SafeInvoke(); // does nothing act = delegate {Console.WriteLine("hi");} act.SafeInvoke(); // writes "hi"
在事件的情况下,这也具有消除竞争条件的优点,即不需要临时variables。 所以通常你需要:
var handler = SomeEvent; if(handler != null) handler(this, EventArgs.Empty);
但是:
public static void SafeInvoke(this EventHandler handler, object sender) { if(handler != null) handler(sender, EventArgs.Empty); }
我们可以简单地使用:
SomeEvent.SafeInvoke(this); // no race condition, no null risk
你正在寻找的是空条件 (而不是“合并”)运算符: ?.
。 它从C#6开始提供。
你的例子是obj?.SomeMethod();
。 如果obj为null,则不会发生任何事情。 当方法有参数时,例如obj?.SomeMethod(new Foo(), GetBar());
如果obj
为null,则不会评估参数,如果评估参数会产生副作用,则该参数很重要。
链接是可能的: myObject?.Items?[0]?.DoSomething()
快速扩展方法:
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(); } }
例:
string str = null; str.IfNotNull(s => Console.Write(s.Length)); str.IfNotNull(s => Console.Write(s.Length), () => Console.Write("null"));
或者可选地:
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)); }
例:
string str = null; Console.Write(str.IfNotNull(s => s.Length.ToString()); Console.Write(str.IfNotNull(s => s.Length.ToString(), () => "null"));
是的,在C#6.0中 – https://msdn.microsoft.com/en-us/magazine/dn802602.aspx 。
object?.SomeMethod()
事件可以用一个永远不会被删除的空的默认委托进行初始化:
public event EventHandler MyEvent = delegate { };
没有必要的空检查。
[ 更新 ,感谢Bevan指出了这一点]
但要注意可能的性能影响。 我所做的一个快速微型基准testing表明,使用“默认代理”模式时,处理没有订阅者的事件速度要慢2-3倍。 (在我的双核2.5GHz笔记本电脑上,这意味着279毫秒:785毫秒提高5000万未订阅的事件)。 对于应用热点,这可能是一个需要考虑的问题。
这篇由伊恩·格里菲斯(Ian Griffiths)撰写的文章给出了两个不同的解决scheme,他总结的是不应该使用的巧妙技巧。
像一个Cerate扩展方法build议并不真正解决竞争条件的问题,而是隐藏它们。
public static void SafeInvoke(this EventHandler handler, object sender) { if (handler != null) handler(sender, EventArgs.Empty); }
如上所述,这个代码是与临时variables解决scheme相当的优雅,但…
两个问题都有可能,那就是事件的子事件可能会在事件取消订阅之后被调用 。 这是可能的,因为在委托实例复制到tempvariables(或者在上面的方法中作为parameter passing)之后,但在调用delegate之前,取消订阅可能发生。
一般来说,在这种情况下,客户端代码的行为是不可预知的:组件状态不允许已经处理事件通知。 可以用处理它的方式编写客户端代码,但是会给客户端带来不必要的责任。
确保线程安全性的唯一已知方法是使用locking语句作为事件的发送者。 这可确保所有订阅\取消订阅\调用都被序列化。
为了更准确的locking,应该应用到add \ remove事件访问器方法中使用的同一个同步对象,它是默认的“this”。
我同意Kenny Eliasson的回答。 使用扩展方法。 以下是扩展方法和您所需的IfNotNull方法的简要概述。
扩展方法(IfNotNull方法)
也许不会更好,但在我看来更可读的是创build一个扩展方法
public static bool IsNull(this object obj) { return obj == null; }
在C#中有一个鲜为人知的null操作符。 可能有帮助: