取消引用可能为空的引用...我的代码可以简化吗?

Dereference of a possibly null reference ... Can my code be simplified?

我的项目是 C# 中的 .Net-6 Blazor WebAssembly(托管)。可以简化我的代码以避免出现可为 null 的警告吗? 我想要来自 ApplicationUser 对象的身份名称(变量为“_Name”)的页面变量中的此人的客户 ID。谢谢

List<Person> listPersons = (List<Person>)(await PService.GetPersons()).ToList();
Person oPerson = new Person();
if (listPersons != null){
    oPerson = (Person)listPersons.Where(p => p.Name!.Equals(_Name)).FirstOrDefault();
}
if (oPerson != null) {
    _UID_CUSTOMER = oPerson.UID_CUSTOMER;
}

您可以通过从 csproj 文件中删除此设置来避免可为 null 的警告

<Nullable>enable</Nullable>

并有这些设置

<PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>    
    <ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

当您使用 Nullable Reference Type feature 时,您需要考虑对于每个(参考)变量,该变量是否允许空值。

这与值类型没有什么不同,例如 int。你不会做

int a = 5;
if (a != null) { // ... }

因为 a 永远不能为空。您需要使用 int? 数据类型,甚至 allow a 为 null。

诚然,有一些方法可以破坏可空引用类型功能 - 例如忽略警告。


让我们使用您的代码并解决一些问题。我会添加行号。

1  List<Person> listPersons = (List<Person>)(await PService.GetPersons()).ToList();
2  Person oPerson = new Person();
3  if (listPersons != null){
4      oPerson = (Person)listPersons.Where(p => p.Name!.Equals(_Name)).FirstOrDefault();
5  }
6  if (oPerson != null) {
7      _UID_CUSTOMER = oPerson.UID_CUSTOMER;
8  }

第 1 行

await PService.GetPersons() return 和 IEnumerable<Person>。因为没有 ?,这意味着整个对象不能为空。此外,每个元素(流中的每个 Person 对象)都不能为空。如果您真的希望 PService.GetPersons() 给您数据 null,则 return 类型将是 Task<IEnumerable<Person>?>.

IEnumerable<Person> 转换为 List<Person> 是危险的。你得到一个 IEnumerable<Person>,一个接口。底层集合 可以 是一个列表,或者它可以是一个数组,或者其他实现 IEnumerable 的东西。当 PService.GetPersons() 的实现发生变化时,将其转换为 List 可能会导致运行时错误。

运行ToList() 转换为 List 之后没有太多意义。它已经是一个列表。事实上,假设您没有获得转换异常,如果 List 为 null,此方法将通过异常。这完全消除了进行空检查的意义。

所以,这是更好的第 1 行:

IEnumerable<Person> people = await PSService.GetPersons();
  • 对“人”使用正确的复数形式。
  • 保持类型 IEnumerable<Person>,如果您只打算使用一次流,则无需强制转换为 List。

第 2 行

您将 oPerson 的默认值设置为 Person 的新实例,并且数据类型 (Person) 表示它永远不能包含空值。但是,在第 4 行,您使用 FirstOrDefault,其中“默认”将为空。所以我们需要更改数据类型来解决这个问题。

此外,我们将重写第 4 行,使第 4 行始终运行,而第 2 行的变量初始化是不必要的。

事实上,这一整行是不需要的,因为它只是变量名。所以删除它。

第 3 行和第 5 行

检查 listPersons(现在称为 people)的可空性没有意义,因为您已经告诉编译器它不能为空。删除那些行。

第 4 行

在您拥有 Name!.Equals() 的地方。 ! 是“空宽恕”运算符。问题在于,如果 Name 为 null,则 .Equals() 将抛出异常。将 .Equals 替换为 ==。 (这都是假设 Name 的数据类型是 string?)。

最后的演员表也没有必要。 FirstOrDefault 将 return 变成 Person(或 null),因此将其转换为相同的数据类型是浪费。

Person? oPerson = people.FirstOrDefault(p => p.Name == _Name);

旁注,我不同意将 FirstOrDefault 中的“默认”值设为 Person 的新实例。我的意见是 FirstOrDefault 的默认值应该为空。这对我来说对您的代码具有语义意义。您正在查看列表以找到匹配的人。如果你找不到一个,那么你会得到 null,而不是一些新的空人。

第 6、7 和 8 行

这些都很好。

但是,如果 _UID_CUSTOMER 的值在执行这些行之前已经为空,则可以简化这些行。在这种情况下,所有行都可以替换为:

_UID_CUSTOMER = oPerson?.UID_CUSTOMER;

这意味着:

  • 如果 oPerson 为 null,则使用 null
  • 如果 oPerson 不为空,则使用 UID_CUSTOMER
  • 的值

同样,只有当您在该行执行之前不关心 _UID_CUSTOMER 的值时才有效。如果只想在oPerson不为null时覆盖_UID_CUSTOMERonly,改回if语句


所以,把它们放在一起你会得到

IEnumerable<Person> people = await PSService.GetPersons();
Person? oPerson = people.FirstOrDefault(p => p.Name == _Name);
_UID_CUSTOMER = oPerson?.UID_CUSTOMER;