C# 中如果不为 null 则调用方法

问题描述 投票:0回答:11

是否可以以某种方式缩短此声明?

if (obj != null)
    obj.SomeMethod();

因为我碰巧写了很多这样的东西,这很烦人。我唯一能想到的就是实现 Null Object 模式,但这不是我每次都能做的,而且它当然不是缩短语法的解决方案。

还有类似的事件问题,其中

public event Func<string> MyEvent;

然后调用

if (MyEvent != null)
    MyEvent.Invoke();
c# null
11个回答
221
投票

从 C# 6 开始,您可以只使用:

MyEvent?.Invoke();

或:

obj?.SomeMethod();

?.
是空传播运算符,当操作数为
.Invoke()
时,会导致
null
短路。操作数仅被访问一次,因此不存在“检查和调用之间值变化”问题的风险。

===

在 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"

在事件的情况下,这样做的优点是还可以消除竞争条件,即您不需要临时变量。所以通常你需要:

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

32
投票

您正在寻找的是空条件(不是“合并”)运算符:

?.
。从 C# 6 开始可用。

你的例子是

obj?.SomeMethod();
。如果 obj 为 null,则不会发生任何事情。当方法有参数时,例如如果
obj?.SomeMethod(new Foo(), GetBar());
为 null,则不会评估参数,如果评估参数会产生副作用,则这一点很重要。
并且可以链接:

obj

    


12
投票

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)); }



5
投票

string str = null; Console.Write(str.IfNotNull(s => s.Length.ToString()); Console.Write(str.IfNotNull(s => s.Length.ToString(), () => "null"));

无需进行空值检查。

[

更新

,感谢 Bevan 指出这一点] 但请注意可能的性能影响。我所做的快速微基准测试表明,使用“默认委托”模式时,处理没有订阅者的事件会慢 2-3 倍。 (在我的双核 2.5GHz 笔记本电脑上,这意味着 279 毫秒:筹集 5000 万个未订阅事件需要 785 毫秒。)。对于应用程序热点,这可能是一个需要考虑的问题。


5
投票
https://learn.microsoft.com/en-us/archive/msdn-magazine/2014/october/csharp-the-new-and-improved-csharp-6-0

public event EventHandler MyEvent = delegate { };



3
投票
Ian Griffiths 的这篇

文章针对该问题提供了两种不同的解决方案,他认为这些解决方案是你不应该使用的巧妙技巧。


2
投票

object?.SomeMethod()

如上所述,这段代码与带有临时变量的解决方案是优雅的等价物,但是......

两者的问题是

事件的订阅者可能会在取消事件订阅后被调用

。这是可能的,因为取消订阅可能会在委托实例复制到临时变量(或作为上述方法中的参数传递)之后、调用委托之前发生。 一般来说,在这种情况下客户端代码的行为是不可预测的:组件状态已经不允许处理事件通知。可以按照处理它的方式编写客户端代码,但这会给客户端带来不必要的责任。

确保线程安全的唯一已知方法是对事件发送者使用锁定语句。这确保所有订阅\取消订阅\调用都被序列化。

为了更准确,应该将锁定应用于添加中使用的同一同步对象 emove 事件访问器方法,默认为“this”。


2
投票

扩展方法(IfNotNull 方法)


1
投票

public static void SafeInvoke(this EventHandler handler, object sender) { if (handler != null) handler(sender, EventArgs.Empty); }



-1
投票

public static bool IsNull(this object obj) { return obj == null; }

然后我像下面一样使用它。

public static class ObjectExtensions { public static void With<T>(this T value, Action<T> todo) { if (value != null) todo(value); } }



-5
投票

http://weblogs.asp.net/scottgu/archive/2007/09/20/the-new-c-null-coalescing-operator-and-using-it-with-linq.aspx

© www.soinside.com 2019 - 2024. All rights reserved.