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

3

我的项目是使用C#编写的.Net-6 Blazor WebAssembly(托管)。是否可以简化我的代码以避免可空警告?我想要将ApplicationUser对象的Identity Name(变量为'_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;
}

3
这段代码有问题。listPersons.Where(...) 会返回一个 IEnumerable<Person> 类型,你不能将其转换为 (Person) 类型。 - Matthew Watson
1
使用 Where 过滤 listPersons,会得到一个 IEnumerable<Person> 而不是一个 Person。看起来你想要使用 .FirstOrDefault 或者 .SingleOrDefault 而不是 .Where - ProgrammingLlama
1
如果在 listPersons 的数据类型中没有包含 ?(例如 List<Person>?),那么你的意思是 listPersons 永远不可能为空。请问 PService.GetPersons() 的返回类型(特别是该返回类型的可空性)是什么? - gunr2171
1
那么你为什么要进行空值检查呢?如果你的第一个方法调用返回 IEnumerable<Person>,那就意味着它不可能为空。事实上,即使它确实为空,在 if 语句运行之前 .ToList() 就会抛出异常。你对 C# 中的可空引用类型特性了解多少? - gunr2171
1
Name的数据类型是什么?是string还是string??您允许Name保存空值吗? - gunr2171
显示剩余3条评论
2个回答

4
当您使用可空引用类型特性时,需要考虑每个(引用)变量是否允许为 null。
这与值类型(如int)没有区别。您不会这样做。
int a = 5;
if (a != null) { /* ... */ }

因为a永远不可能为空。你需要使用int?数据类型,甚至允许a为空。

当然,有一些方法可以打破可空引用类型的特性 - 比如忽略警告。


让我们拿出你的代码并修复一些问题。我会添加行号。

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  }

第一行

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

IEnumerable<Person> 强制转换为 List<Person> 是危险的。您得到了一个接口 IEnumerable<Person>。底层集合可能是 List,也可能是 Array,或者其他实现了 IEnumerable 的东西。将其强制转换为 List 可能会导致运行时错误,当 PService.GetPersons() 的实现发生更改时。

在将对象转换为列表后,运行ToList()没有太大意义。它已经是一个列表了。实际上,假设您没有收到强制转换异常,如果列表为空,此方法将抛出异常。这消除了进行空检查的意义。
因此,这是更好的第一行:
IEnumerable<Person> people = await PSService.GetPersons();
  • 使用正确的“人”复数形式。
  • 保持类型为 IEnumerable<Person>,如果您只打算使用流一次,则无需强制转换为 List。

第 2 行

您将 oPerson 的默认值设置为 Person 的新实例,并且数据类型 (Person) 表示它永远不能保存空值。然而,在第 4 行中,您使用了 FirstOrDefault,其中“Default”将为 null。因此,我们需要更改数据类型以解决这个问题。

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

事实上,整行都是不必要的,因为它只是变量名。所以把它删掉。

第 3 行和第 5 行

检查 listPersons(现在称为 people)是否为空没有意义,因为您告诉编译器它不能为 null。删除这些行。

第 4 行

Name!.Equals() 中,! 是“空值容错”运算符。问题在于,如果 Name 为 null,则 .Equals() 将抛出异常。请用 == 替换 .Equals。(这一切都假设 Name 的数据类型是 string?)。
最后的强制转换也是不必要的。 FirstOrDefault 将返回一个 Person(实际上是一个 Person?),因此将其强制转换为相同的数据类型是浪费的。
Person? oPerson = people.FirstOrDefault(p => p.Name == _Name);

侧记,我不同意将FirstOrDefault的“默认”值设为新的Person实例。我认为FirstOrDefault的默认值应该是null。对我来说,这使你的代码在语义上更有意义。你正在查找一个匹配的人物名单。如果找不到,则得到null,而不是一些新的空人。
第6、7和8行
这些很好。
但是,如果_UID_CUSTOMER的值在执行这些行之前已经是null,则可以简化这些行。在这种情况下,所有这些行都可以被替换为:
_UID_CUSTOMER = oPerson?.UID_CUSTOMER;

这意味着:
  • 如果 oPerson 为空,就使用 null
  • 如果 oPerson 不为空,则使用 UID_CUSTOMER 的值
再次强调,这仅在此行执行之前不关心 _UID_CUSTOMER 的值时才有效。如果您只想在 oPerson 不为空时覆盖 _UID_CUSTOMER,请将其改回 if 语句。

因此,将所有内容组合在一起,您会得到

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

感谢您的精彩解释!我完全明白了。从3个小时前开始,我将我的代码改成与您的非常相似,并将数据库和模型更改为讨论下面字段的非空值。我将复制您的代码以替换我的代码。谢谢。 - John D

3
您可以通过从csproj文件中删除此设置来避免可空警告。
<Nullable>enable</Nullable>

并且具有以下设置

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

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