猫王运算符(Nullsave Dereference Operator)是否导致空引用异常?
Is the Elvis Operator (Nullsave Dereference Operator) causing null reference exceptions?
在包含 elvis 运算符(即 nullsafe 取消引用运算符;?.)的表达式上调用扩展方法时,结果 null 不会按预期传递给扩展方法。本质上,它可能会导致意外的空引用异常。下面是一个演示这一点的程序:
class Program
{
static void Main(string[] args)
{
string nil = null;
foreach (var c in ((nil?.ToCharArray()).EmptyIfDefault())) { }; // works
foreach (var c in (nil?.ToCharArray().EmptyIfDefault())) { }; // nullref
}
}
public static class Utility
{
public static char[] EmptyIfDefault(this char[] target)
{
return target ?? new char[0];
}
}
有人知道这种行为是设计使然吗?注意没有?在 ToCharArray() 和 EmptyIfDefault 之间。如果有的话,我会理解当前的行为。现在,这似乎是一个错误。 (向 Microsoft 报告此问题的正确方法是什么?)
对于看到相同行为的其他人:额外的大括号似乎可以阻止它。
(顺便说一下:这是我正在使用的实际 EmptyIfNull:)
public static IEnumerable<TTarget> EmptyIfNull<TTarget>(this IEnumerable<TTarget> target)
{
return target ?? Enumerable.Empty<TTarget>();
}
编辑 我会将下面给出的答案包含在我的问题中:
这与一个常见的陷阱有关:
var txt = "I am " +
age>=18 ? "mature" : "not old" +
" enough.";
这也解释为
var txt = "I am " +
age >= 18
? "mature"
: ("not old" + " enough.");
重写时,没有大括号的行为是有意义的:
foreach(var c in
nil == null
? null
: nil.ToCharArray().EmptyIfDefault()) { }; // nullref
虽然一开始不直观,但这绝对不是错误。你得到一个 NullReferenceException
因为你试图遍历 null
(它不会在评估表达式时抛出异常)
我们来看这个例子:
var t = nil?.ToCharArray().EmptyIfNull();
以上将不会调用EmptyIfNull
,因为nil
将是null
并且方法链将短路到return null
.
也就是我们可以把上面的写成:
IEnumerable<char> t;
if (nil != null)
t = nil.ToCharArray().EmptyIfNull();
else
t = null;
请注意,EmptyIfNull
仅在满足初始条件(即 nil
不为空)时才执行 。
现在,为什么要用括号固定它?
var t = (nil?.ToCharArray()).EmptyIfNull();
这可以重写为:
IEnumerable<char> t;
IEnumerable<char> temp;
if (nil != null)
temp = nil.ToCharArray();
else
temp = null;
t = temp.EmptyIfNull();
看到短路行为仅适用于内部表达式 - 然后我们总是对结果调用EmptyIfNull
。
在包含 elvis 运算符(即 nullsafe 取消引用运算符;?.)的表达式上调用扩展方法时,结果 null 不会按预期传递给扩展方法。本质上,它可能会导致意外的空引用异常。下面是一个演示这一点的程序:
class Program
{
static void Main(string[] args)
{
string nil = null;
foreach (var c in ((nil?.ToCharArray()).EmptyIfDefault())) { }; // works
foreach (var c in (nil?.ToCharArray().EmptyIfDefault())) { }; // nullref
}
}
public static class Utility
{
public static char[] EmptyIfDefault(this char[] target)
{
return target ?? new char[0];
}
}
有人知道这种行为是设计使然吗?注意没有?在 ToCharArray() 和 EmptyIfDefault 之间。如果有的话,我会理解当前的行为。现在,这似乎是一个错误。 (向 Microsoft 报告此问题的正确方法是什么?)
对于看到相同行为的其他人:额外的大括号似乎可以阻止它。
(顺便说一下:这是我正在使用的实际 EmptyIfNull:)
public static IEnumerable<TTarget> EmptyIfNull<TTarget>(this IEnumerable<TTarget> target)
{
return target ?? Enumerable.Empty<TTarget>();
}
编辑 我会将下面给出的答案包含在我的问题中:
这与一个常见的陷阱有关:
var txt = "I am " +
age>=18 ? "mature" : "not old" +
" enough.";
这也解释为
var txt = "I am " +
age >= 18
? "mature"
: ("not old" + " enough.");
重写时,没有大括号的行为是有意义的:
foreach(var c in
nil == null
? null
: nil.ToCharArray().EmptyIfDefault()) { }; // nullref
虽然一开始不直观,但这绝对不是错误。你得到一个 NullReferenceException
因为你试图遍历 null
(它不会在评估表达式时抛出异常)
我们来看这个例子:
var t = nil?.ToCharArray().EmptyIfNull();
以上将不会调用EmptyIfNull
,因为nil
将是null
并且方法链将短路到return null
.
也就是我们可以把上面的写成:
IEnumerable<char> t;
if (nil != null)
t = nil.ToCharArray().EmptyIfNull();
else
t = null;
请注意,EmptyIfNull
仅在满足初始条件(即 nil
不为空)时才执行 。
现在,为什么要用括号固定它?
var t = (nil?.ToCharArray()).EmptyIfNull();
这可以重写为:
IEnumerable<char> t;
IEnumerable<char> temp;
if (nil != null)
temp = nil.ToCharArray();
else
temp = null;
t = temp.EmptyIfNull();
看到短路行为仅适用于内部表达式 - 然后我们总是对结果调用EmptyIfNull
。