方法调用,如果在 C # 中不为空

有没有可能以某种方式缩短这句话?

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

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

事件也有类似的问题

public event Func<string> MyEvent;

然后援引

if (MyEvent != null)
MyEvent.Invoke();
272353 次浏览

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

在事件的情况下,这样做的好处是还可以删除竞态条件,也就是说,您不需要临时变量。所以通常你需要:

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

也许不是更好,但在我看来,更可读的是创建一个扩展方法

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

事件可以用从不删除的空默认委托初始化:

public event EventHandler MyEvent = delegate { };

不需要空检查。

[ 更新,感谢 Bevan 指出这一点]

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

像建议的那样确定扩展方法并不能真正解决竞态条件的问题,而是隐藏它们。

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

如前所述,这段代码优雅地等同于使用临时变量的解决方案,但是..。

这两个 事件的订阅者有可能在它从事件中取消订阅之后被调用的问题。这是可能的,因为在将委托实例复制到临时变量(或在上述方法中作为参数传递)之后,但在调用委托之前,可能会发生取消订阅。

通常,在这种情况下,客户机代码的行为是不可预测的: 组件状态不允许已经处理事件通知。按照处理它的方式编写客户机代码是可能的,但是这会给客户机带来不必要的责任。

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

为了更加准确,应该将锁应用到同一个同步对象中使用的添加删除事件访问器方法,这是默认的“ this”。

我同意肯尼 · 埃利亚松的回答。使用扩展方法。下面是扩展方法和所需的 IfNotNull 方法的简要概述。

扩展方法(IfNotNull 方法)

快速扩展方法:

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

您要寻找的是 无条件(而不是“合并”)操作符: ?.

你的例子是 obj?.SomeMethod();。如果 obj 为 null,则不会发生任何操作。当方法有参数时,例如 obj?.SomeMethod(new Foo(), GetBar());,如果 obj为空,参数就不会被计算,如果计算参数会产生副作用,这就很重要了。

链接是可能的: myObject?.Items?[0]?.DoSomething()

是的,在 C # 6.0—— https://msdn.microsoft.com/en-us/magazine/dn802602.aspx

object?.SomeMethod()

我已经制作了我使用的这个通用扩展。

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

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

string myString = null;
myString.With((value) => Console.WriteLine(value)); // writes nothing
myString = "my value";
myString.With((value) => Console.WriteLine(value)); // Writes `my value`