如果 null 合并运算符应该防止出现 null 值,为什么 Visual Studio 或 ReSharper 会建议一个可能的 null 解决方案?

If the null-coalescing operator is supposed to protect against null values, why would Visual Studio or ReSharper suggest a possible null solution?

这是我的通用函数

int SomeArray(IEnumerable<int> array, int size) 
{
    int product = 1
    for (var i = 0; i < size; i++)
    {
        product *= array[i];
    }
    return product
}

Visual Studio 或 ReSharper,我不确定是哪个,建议我通过将函数更改为

来更改 array[i] 以枚举为数组
var enumerable = array as int[] ?? array.ToArray();
for (var i = 0; i < size; i++)
{
    product *= enumerable[i];
}

我看到的问题是传入的 array 可能为空,并且 Visual Studio 确实警告可能会抛出 System.NullReferenceException

真正的问题是 null 合并运算符的想法是,如果左侧的操作数为 null,则将使用右侧的操作数。

如果我这样做 var enumerable = array as int[] ?? array.ToArray() ?? new int[size]; new int[size] 被标记为不需要,因为根据 Visual Studio 左边的内容永远不会为空。当我可以清楚地强制传入的数组为空时。

所以我没有得到什么?

如果 arraynull 那么该行代码将在那个 null 值上调用 array.ToArray,并且它会抛出参数 null 异常。

ToArray 永远不会 return 一个 null 值(即使提供了一个 null 值),这就是为什么你的数组构造函数永远不能被调用的原因。

这个表达式

var enumerable = array as int[] ?? array.ToArray();

永远不能为 null(根据 Resharper),因为如果 array 为 null - 它会抛出异常,并且 Resharper 知道 ToArray 永远不会 return null( Resharper 知道有一个特殊的方法数据库不能 return null,你可以用 NotNull 属性标记你自己的方法。

如果您对那个 Resharper "database"(称为外部注释,它只是 xml 文件)感兴趣,这里是 source 您可以看到的地方:

 <member
    name="M:System.Linq.Enumerable.ToArray``1(System.Collections.Generic.IEnumerable{``0})">
     <parameter name="source">
       <attribute ctor="M:JetBrains.Annotations.NotNullAttribute.#ctor" />
     </parameter>
     <attribute ctor="M:JetBrains.Annotations.NotNullAttribute.#ctor" />          
</member>

在哪里可以看到 ToArray() 被标记为 NotNull 属性。因此,Resharper 确定 ToArray() 不能 return null(虽然理论上可以)。

如果你想使用你的表达式,你需要检查数组是否为空,像这样(如果你可以使用 C# 6):

var enumerable = array as int[] ?? array?.ToArray() ?? new int[size];

或者这样:

var enumerable = array as int[] ?? (array != null ? array.ToArray() : new int[size]);