c# 8 foreach 中的 NonNullable 引用类型产生奇怪的警告消息

c# 8 NonNullable Reference Types in foreach produce odd warning message

写以下代码时

 foreach (var item in (IEnumerable)searchAndReplaceData.RadGridView.ItemsSource ?? Enumerable.Empty<object>())
 {
      object test = item;
      ...

我收到一条关于行 object test=item;:

的警告

CS 8600: Converting null literal or possible null value to non-nullable type.

你会如何处理这种情况?我必须写一个

if (item==null) continue;

每次在nullable-save代码中确保item不为null?有没有更优雅的方式来处理这个问题?

多亏了好心人的评论,我才可以更细化问题的定义。

简短的回答是:是的,您必须进行 if (item==null) continue; 检查以避免该警告并保持安全。

由于编译器并不总是显示警告 - 至少我所期望的,这里有一些示例代码可以帮助其他人理解空引用检查的当前限制。特别是在使用尚未检查空引用的第 3 方代码时。

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System;

#nullable disable
public static class NullableDisabled3rdPartyClass
{
    public static object UnclearNullableStateEnumerableAsObject()
    {
        IEnumerable<object> objects = new object[] { 1, 2, 3, null };
        return objects;
    }
}

#nullable enable

public class Program
{
    public static void Main()
    {
        foreach (var thing in (IEnumerable)NullableDisabled3rdPartyClass.UnclearNullableStateEnumerableAsObject() ?? Enumerable.Empty<object>())
        {
            object item = thing; //Shows the warning CS8600
            //Console.WriteLine(item.ToString()); //crashes
        }

        //does not show any warning but also crashes on thing.toString
        foreach (var thing in (IEnumerable<object>)NullableDisabled3rdPartyClass.UnclearNullableStateEnumerableAsObject() ?? Enumerable.Empty<object>())
        {
            object item = thing; //no warning
            //Console.WriteLine(thing.ToString()); //crashes
        }

        //does not help either
        var x = NullableDisabled3rdPartyClass.UnclearNullableStateEnumerableAsObject() as IEnumerable<object>;
        foreach (var thing in x ?? Enumerable.Empty<object>())
        {
            object item = thing; //no warning
            //Console.WriteLine(item.ToString()); //crashes
        }

        //shows no warning and does not crash
        foreach (var thing in (IEnumerable)NullableDisabled3rdPartyClass.UnclearNullableStateEnumerableAsObject() ?? Enumerable.Empty<object>())
        {
            if (thing == null) continue;
            object item = thing;
            Console.WriteLine(item.ToString());
        }
    }
}

枚举 IEnumerable 时的情况似乎与您在 .NET Core 3.0 和 .NET Framework 中发布的代码一样,枚举产生 object,而在 .NET Core 中3.1 枚举产生 object?,这就是您收到警告的原因。

IEnumerable 产生 object? 确实有意义,因为没有迹象表明内容的可空性。

如果您认为集合中可能确实有 null,那么您需要决定如何处理它们。如果你只是想忽略它们,你可以过滤掉它们。这基本上等同于您的 if (item == null) continue; 行。

var maybeSource = (IEnumerable<object?>?)searchAndReplaceData.RadGridView.ItemsSource;
var maybeNullItems = maybeSource ?? Enumerable.Empty<object?>();

var items = maybeNullItems.Where(maybeNullItem => maybeNullItem != null);

// items is automatically inferred to be IEnumerable<object>

foreach (var item in items) {
   ...

或者如果您认为任何项目实际上都不可能是 null,您应该改为抛出(这样您会立即知道 if/when 您错了)。

var maybeSource = (IEnumerable<object?>?)searchAndReplaceData.RadGridView.ItemsSource;
var maybeNullItems = maybeSource ?? Enumerable.Empty<object?>();

var someAreNull = maybeNullItems.Any(maybeNullItem => maybeNullItem == null);
if (someAreNull)
   throw new ShouldNotBeNullException(); // made-up exception for this purpose

var items = (IEnumerable<object>)maybeNullItems;

foreach (var item in items) {
   ...

请注意,可以编写各种辅助方法来大量清理此类代码。