Elvis运算符(Nullsave解引用运算符)是否导致空引用异常?

时间:2016-04-17 23:46:42

标签: c# null operators dereference null-coalescing-operator

在包含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

1 个答案:

答案 0 :(得分:5)

虽然起初它不直观,但它绝对不是一个错误。您正在获得NullReferenceException,因为您正在尝试迭代null(在评估表达式时不会抛出异常)

让我们来看看这个例子:

var t = nil?.ToCharArray().EmptyIfNull();

上述调用EmptyIfNull,因为nil将为null,方法链将短路以返回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