c# - 猫王运算符(Nullsave Dereference Operator)是否导致空引用异常?

标签 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

最佳答案

虽然起初不直观,但绝对不是错误。你得到一个 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

关于c# - 猫王运算符(Nullsave Dereference Operator)是否导致空引用异常?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36683480/

相关文章:

c# - 先EF数据库,重命名实体表名

android - 将具有空值的字符串作为参数传递时, Volley 字符串请求错误

c++ - 在重载 =operator 中返回引用的目的是什么

c# - 运算符重载?

c# - 如何合并2个字典

c# - 在 WebClient.DownloadFile 之前检查文件是否存在

ios - JSON 解析并收到 : fatal error: unexpectedly found nil while unwrapping an Optional value

php - 如果我将 NULL 赋给变量,isset() 会返回 false 吗?

具有五对的 scala map += 运算符

c# - 使用 SSH.NET 查找具有特定文件名的最新 SFTP 文件