在包含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,并且方法链将短路以返回null

也就是说,我们可以这样写:

IEnumerable<char> t;
if (nil != null)
    t = nil.ToCharArray().EmptyIfNull();
else
    t = null;


请注意,EmptyIfNull仅在初始条件通过时执行(即,nil不为null)。

现在,为什么用括号将其修复?

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


可以重写为:

IEnumerable<char> t;
IEnumerable<char> temp;
if (nil != null)
    temp = nil.ToCharArray();
else
    temp = null;
t = temp.EmptyIfNull();


请注意,短路行为仅适用于内部表达式-然后我们总是对结果调用EmptyIfNull

09-25 22:31