是否有可能以某种方式缩短此声明?
if (obj != null)
obj.SomeMethod();
因为我碰巧写了很多,这很烦人。我唯一能想到的就是实现Null Object模式,但这并不是我每次都能做的事情,并且它当然不是缩短语法的解决方案。
以及类似的事件问题
public event Func<string> MyEvent;
然后调用
if (MyEvent != null)
MyEvent.Invoke();
是否有可能以某种方式缩短此声明?
if (obj != null)
obj.SomeMethod();
因为我碰巧写了很多,这很烦人。我唯一能想到的就是实现Null Object模式,但这并不是我每次都能做的事情,并且它当然不是缩短语法的解决方案。
以及类似的事件问题
public event Func<string> MyEvent;
然后调用
if (MyEvent != null)
MyEvent.Invoke();
Answers:
从C#6开始,您可以使用:
MyEvent?.Invoke();
要么:
obj?.SomeMethod();
的?.
是null传播算子,并且将导致.Invoke()
要被短路时的操作数是null
。操作数仅被访问一次,因此没有“检查和调用之间的值更改”问题的风险。
===
在C#6之前,否:除零例外,没有null安全魔术。扩展方法-例如:
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
?.
-在VB14和上述
快速扩展方法:
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"));
可以使用永远不会删除的空默认委托来初始化事件:
public event EventHandler MyEvent = delegate { };
无需进行空检查。
[ 更新,感谢Bevan指出了这一点]
但是,请注意可能的性能影响。我所做的快速微型基准测试表明,使用“默认委托”模式时,没有订阅者的事件处理速度要慢2-3倍。(在我的双核2.5GHz笔记本电脑上,这意味着279ms:785ms可以引发5000万个未订阅的事件。)对于应用程序热点,这可能是一个要考虑的问题。
是的,在C#6.0中-https: //msdn.microsoft.com/zh-cn/magazine/dn802602.aspx。
object?.SomeMethod()
像上面建议的那样,采用分块扩展方法并不能真正解决种族问题,而是将它们隐藏起来。
public static void SafeInvoke(this EventHandler handler, object sender)
{
if (handler != null) handler(sender, EventArgs.Empty);
}
如前所述,此代码与带有临时变量的解决方案非常相似,但是...
两者的问题在于,在事件取消订阅之后,该事件的子订阅可能会被称为。之所以可能这样做,是因为在将委托实例复制到temp变量之后(或在上面的方法中作为参数传递)但在调用委托之前,可能会发生取消订阅的情况。
通常,在这种情况下,客户端代码的行为是不可预测的:组件状态已不允许处理事件通知。可以以处理客户端代码的方式编写客户端代码,但这会给客户端带来不必要的责任。
确保线程安全的唯一已知方法是对事件的发送者使用lock语句。这样可以确保所有订阅\取消订阅\调用被序列化。
为了更准确,应该将锁应用于在add \ remove事件访问器方法中使用的同一同步对象,默认情况下为'this'。
我同意肯尼·埃里亚森的回答。使用扩展方法。这是扩展方法和所需的IfNotNull方法的简要概述。
也许不是更好,但我认为更具可读性的是创建一个扩展方法
public static bool IsNull(this object obj) {
return obj == null;
}
return obj == null
意思 会是什么返回
obj
是null
该方法将返回true
,我想。
null
吗?我很确定这不适用于类型自己的方法,因此我怀疑它是否也可以与扩展方法一起使用。我相信检查对象是否 null
存在的最佳方法是obj is null
。不幸的是,检查一个对象是否不是null
不需要用括号括起来,这是不幸的。