方法调用,如果在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; }