将null字面值或可能为null的值转换为非可空类型

33

是否可能解决此警告:

将 null 字面量或可能为 null 的值转换为非可空类型。

而不需要抑制此 C# 代码的警告?

 List<PropertyInfo> sourceProperties = sourceObject.GetType().GetProperties().ToList<PropertyInfo>();
            List<PropertyInfo> destinationProperties = destinationObject.GetType().GetProperties().ToList<PropertyInfo>();

            foreach (PropertyInfo sourceProperty in sourceProperties)
            {
                if (!Equals(destinationProperties, null))
                {
#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
                    PropertyInfo destinationProperty = destinationProperties.Find(item => item.Name == sourceProperty.Name);
#pragma warning restore CS8600 // Converting null literal or possible null value to non-nullable type.

                   
                }
            }

使用反射的代码。

在此输入图像描述

我正在使用 Visual Studio 2019 和 .NET Core 3.1。

4个回答

56
Find()在找不到目标时可能返回null。因此,destinationProperty可能会变成null。 因此解决方案是将其声明为可空类型:
PropertyInfo? destinationProperty = ...

或者抛出异常:

PropertyInfo destinationProperty = ...Find() ?? throw new ArgumentException(...)

5
如果PropertyInfo是一个类,那么它允许空值。因此,我不明白为什么它是必需的。需要更明确的代码吗? - Carlos Iglesias
2
@Carlos 这个编译器警告是从可空上下文中发出的,请参见 https://learn.microsoft.com/en-us/dotnet/csharp/nullable-references#nullable-contexts。编译器警告说 OP 的变量被声明为非可空 (PropertyInfo destinationProperty 没有 ?),但 Find() 方法可能返回 null 值。这是 C# 8 的一个特性。 - CodeCaster
1
这是一个新的需要学习的东西!谢谢!现在我知道在 C# 8 中,我们需要显式地使类可空。否则它们不应该为空(我认为这对这些 null 有更多的控制):D 奇怪但值得一试。 - Lam Le

3
您可以采用 @codecaster 提出的解决方案之一,或者根据 Microsoft 文档 的描述,您也可以通过在右侧添加空值忽略运算符 ! 来禁用此警告。
PropertyInfo destinationProperty = destinationProperties.Find(item => item.Name == sourceProperty.Name)!; //<------ note the "!" operator at the end of the line

1
通过添加 !,你告诉编译器:“相信我,这不会是 null”。只有当你确定时才这样做,否则在使用此现在为 null 的变量时可能会引起问题。 - CodeCaster

0
你可以做一件事情,而不是使用类型 'PropertyInfo' PropertyInfo destinationProperty = destinationProperties.Find(item => item.Name == sourceProperty.Name);

你可以使用 'var' var destinationProperty = destinationProperties.Find(item => item.Name == sourceProperty.Name);


0

使用默认对象和空合并(??)运算符

C#中的所有引用类型都隐式可为空。添加的是微软的新空检查系统,它试图提醒您可能导致运行时异常的空值出现的情况。原因是微软提出了现在所有内容的"非空"与"可能为空"逻辑的概念。

这意味着,无论引用类型的可为空属性如何,除非明确设置为可为空(MyType? obj),否则它们仍然可能是可能为空的,并触发类似于此的编译警告。然而,即使将类型设置为可为空 ?,它们仍然有可能破坏代码中的非空可能为空类型和逻辑。不要依赖可为空运算符?来解决问题,因为即使使用?运算符的可为空引用类型,在处理非空类型时仍然可能破坏代码。

因此,可为空类型不能解决问题,只是停止警告,因为您告诉编译器现在要期望空值。

更糟糕的是,在.NET中,对于引用类型,我们没有非空操作符(已经过去25年了,我们还是不能创建非空类型吗?)。这将强制避免使用null。我们有??= null coalesce等式操作,但它并不强制进行非空对象的创建。
使用"null-forgiving operator"!也存在同样的问题。它只是抑制了可为空的警告,但并不能解决空指针异常的风险!因此,我尽量避免使用它。不要相信你的代码逻辑,相信你的对象。除非你在处理null并明确检查它们,否则它们不应该是null或可为空。
在处理"可能为null"的情况下,我的解决方案是通过检查null(这可能会停止null检查警告)或计划在某些东西可能为null时分配一个默认对象,如下所示。所有引用类型默认情况下都可能为null,那么为什么要让它们可能破坏你代码的其他部分呢?
我的"默认对象解决方案"具有几个优点:
  1. 保证一个对象(非空值)
  2. 允许在可为空、可能为空和非空类型之间进行安全转换
  3. 避免在其他地方进行空检查
  4. 避免仅为了抑制一种狭窄类型的警告或提示而分配可为空和null-forgiving运算符
  5. 消除了以后访问引用的代码(例如方法)中的所有空警告
  6. 允许开发人员在首次使用时明确编写针对回退情况的代码,而不是以后出现未知、无值、缺失值或错误值
  7. 避免滥用.NET中的未处理异常系统或NullReferenceException调用来处理未知和意外的与空相关的错误。它们总是被避免和处理!

下面,我使用空合并运算符??作为一种快速分配这些对象的方式:

PropertyInfo destinationProperty = 
destinationProperties.Find(item => item.Name == sourceProperty.Name)
?? new PropertyInfo();

我给我的类型分配了一个默认的空对象,这样做的好处是它永远不会因为其他类型的访问而出错,并且它可以完全控制默认对象的行为、显示方式或者类型转换。再也不用担心空值了!
在你的PropertyInfo类中,你也可以为所有的类属性分配默认的非空值,这样它们也有值并且可以进行类型转换、提取等操作。这种解决方案还增加了类型安全性,或者如果你决定将你的类型稍后设置为可为空,并将这些可为空的值与可能为空的逻辑混合使用。你仍然可以明确地使用空值,或者切换到默认对象。它简单地允许你在应用程序中使用默认引用,当与各种其他代码和对象一起使用时,这些引用真正需要默认对象。现在,你可以围绕“默认值”构建逻辑,而不是“未知值”(没有值),这通常是编码世界中null的含义。
无论如何,你都不应该依赖于对可为空的变量赋予null值来进行任何类型的逻辑操作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接