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

C# 方法调用(如果在C中不为null)#,c#,null,C#,Null,是否有可能以某种方式缩短这一陈述 if (obj != null) obj.SomeMethod(); 因为我碰巧写了很多这篇文章,它变得很烦人。我能想到的唯一一件事就是实现Null Object模式,但这不是我每次都能做的,当然也不是缩短语法的解决方案 以及类似的事件问题,在哪里 public event Func<string> MyEvent; 从C#6开始,您只需使用: MyEvent?.Invoke(); 或: ?。是null传播运算符,当操作数为null时,

是否有可能以某种方式缩短这一陈述

if (obj != null)
    obj.SomeMethod();
因为我碰巧写了很多这篇文章,它变得很烦人。我能想到的唯一一件事就是实现Null Object模式,但这不是我每次都能做的,当然也不是缩短语法的解决方案

以及类似的事件问题,在哪里

public event Func<string> MyEvent;
从C#6开始,您只需使用:

MyEvent?.Invoke();
或:

?。
是null传播运算符,当操作数为
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 { };
不需要空检查

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


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

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

< P>在C中有一个鲜为人知的空算子。可能会有帮助:


像一个建议的Cerating扩展方法并不能真正解决种族条件的问题,而是隐藏了它们

public static void SafeInvoke(this EventHandler handler, object sender)
{
    if (handler != null) handler(sender, EventArgs.Empty);
}
如前所述,这段代码相当于使用临时变量的解决方案,但

这两个的问题是,在取消订阅事件后,可能会调用事件的子服务器。这是可能的,因为取消订阅可能发生在委托实例复制到temp变量(或在上述方法中作为参数传递)之后,但在调用委托之前

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

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


更准确地说,锁定应该应用于添加\删除事件访问器方法中使用的相同同步对象,这是默认的“this”。

我同意Kenny Eliasson的回答。使用扩展方法。下面是扩展方法和所需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();
        }
    }
或者:

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

您要查找的是(而不是“合并”)运算符:
?。
。从六世纪开始就有了

您的示例是
obj?.SomeMethod()。如果obj为null,则不会发生任何事情。当方法有参数时,例如,
obj?.SomeMethod(newfoo(),GetBar())obj
为空,则不计算参数,这与计算参数是否会产生副作用有关

链接是可能的:
myObject?.Items?[0]?.DoSomething()
是的,在C#6.0--


我使用了这个通用扩展

公共静态类ObjectExtensions{
公共静态无效(此T值,操作待办事项){
如果(值!=null)todo(值);
}
}
然后我像下面那样使用它

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

我对此有点矛盾。对于操作或事件处理程序(通常更独立)来说,这是有意义的。不过,我不会破坏常规方法的封装。这意味着在一个单独的静态类中创建该方法,我不认为失去封装和降低可读性/代码组织的总体性能值得在本地应用程序中稍作改进readability@tvanfosson-事实上;但我想说的是,这是我所知道的唯一一个可行的例子。这个问题本身就引出了委托/事件的主题。这段代码最终在某个地方生成了一个匿名方法,这确实打乱了任何异常的堆栈跟踪。可以给匿名方法命名吗?;)@MarcGravel这不是称为null条件运算符。@mercu它应该是
?。
-在VB14及以上版本中,我们考虑在C#4中添加一个新运算符:“obj.?SomeMethod()”将表示“如果obj不为null,则调用SomeMethod,否则,返回null”。不幸的是,它不符合我们的预算,所以我们从来没有实施过。@Eric:这个评论仍然有效吗?我在某个地方见过,它有4.0版本?@CharithJ:没有。它从未实现过。@CharithJ:我知道空合并运算符的存在。它不能满足达斯的要求;他想要一个提升到可为空的成员访问操作符。(顺便说一句,您之前的评论给出了空合并运算符的错误表征。您的意思是说“v=m==null?y:m.值可以写为v=m??y”。)对于较新的读者:C#6.0实现?,所以x?.y?.z?.ToString()将在x、y或z为null时返回null,或者在它们都不为null时返回z.ToString()。因此,通过调用空委托可以避免空检查。。。为了节省几次击键,可测量的内存和时间牺牲?YMMV,但对我来说,这是一个糟糕的交易。我也看到了t
  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"));
object?.SomeMethod()
string myString = null;
myString.With((value) => Console.WriteLine(value)); // writes nothing
myString = "my value";
myString.With((value) => Console.WriteLine(value)); // Writes `my value`