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

时间:2009-05-16 12:15:05

标签: c# null

是否有可能以某种方式缩短这种说法?

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

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

与事件类似的问题,其中

public event Func<string> MyEvent;

然后调用

if (MyEvent != null)
    MyEvent.Invoke();

11 个答案:

答案 0 :(得分:133)

从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

答案 1 :(得分:26)

您要找的是Null Conditional(非“合并”)运算符: ?. 。它从C#6开始提供。

您的示例是obj?.SomeMethod();。如果obj为null,则没有任何反应。当该方法具有参数时,例如obj?.SomeMethod(new Foo(), GetBar());如果obj为null,则不评估参数,如果评估参数会产生副作用,则这很重要。

可以链接:myObject?.Items?[0]?.DoSomething()

答案 2 :(得分:10)

快速扩展方法:

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

答案 3 :(得分:4)

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

public event EventHandler MyEvent = delegate { };

不需要进行空检查。

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

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

答案 4 :(得分:4)

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

object?.SomeMethod()

答案 5 :(得分:2)

Ian Griffiths的文章给出了两个不同的解决方案,他得出的结论是你不应该使用的巧妙技巧。

答案 6 :(得分:2)

像建议的那样使用Cerating extention方法并不能真正解决竞争条件问题,而是隐藏它们。

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

如上所述,这段代码与临时变量的解决方案相当优雅,但是......

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

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

确保线程安全性的唯一已知方法是对事件的发送者使用lock语句。这可以确保所有subscriptions \ unsubscriptions \ invocation都被序列化。

为了更准确,锁应该应用于add \ remove事件访问器方法中使用的同一个同步对象,默认为'this'。

答案 7 :(得分:2)

我同意Kenny Eliasson的回答。使用扩展方法。以下是扩展方法和所需的IfNotNull方法的简要概述。

Extension Methods ( IfNotNull method )

答案 8 :(得分:0)

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

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

答案 9 :(得分:0)

我已经做了我使用的通用扩展名。

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`

答案 10 :(得分:-4)

C#中有一个鲜为人知的空操作符,用于此操作。可能会有所帮助:

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