【问题标题】:Dereference of a possibly null reference ... Can my code be simplified?取消引用可能为空的引用...我的代码可以简化吗?
【发布时间】:2022-11-23 01:33:07
【问题描述】:

我的项目是 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;
}

【问题讨论】:

  • 该代码有问题。 listPersons.Where(...) 将返回一个 IEnumerable&lt;Person&gt;,您不能将其转换为 (Person)
  • Where过滤器listPersons,给你一个IEnumerable&lt;Person&gt;而不是Person。看来你想要 .FirstOrDefault 或者 .SingleOrDefault 而不是 .Where
  • 通过在listPersons(例如List&lt;Person&gt;?)的数据类型中没有?,您是说listPersons永远不能为空。 PService.GetPersons() 的返回类型(特别是该返回类型的可空性)是什么?
  • 那么你为什么要进行空检查呢?如果您的第一个方法调用返回IEnumerable&lt;Person&gt;,则意味着它不能为空。事实上,即使它曾是null,.ToList() 会在 if 语句运行之前抛出异常。你对c#中的nullable reference types feature了解多少?
  • Name 的数据类型是什么? stringstring??你允许Name 持有空值吗?

标签: c# blazor-webassembly


【解决方案1】:

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

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

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

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

当然,有一些方法可以破坏 Nullable Reference Type 特性——比如忽略警告。


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

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() 返回 IEnumerable&lt;Person&gt;。因为没有?,这意味着整个对象不能为空。此外,每个元素(流中的每个 Person 对象)都不能为空。如果你真的希望PService.GetPersons()给你数据或者null,返回类型将是Task&lt;IEnumerable&lt;Person&gt;?&gt;

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

ToList()没有多大意义演员表。它已经是一个列表。事实上,假设您没有得到转换异常,如果 List 为空,此方法将抛出异常。这完全消除了进行空检查的意义。

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

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

2号线

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

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

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

3号线和5号线

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

4号线

在你有Name!.Equals()的地方。 ! 是“空宽恕”运算符。问题是如果Name一片空白,那么.Equals() 将抛出异常。将 .Equals 替换为 ==。 (这一切都假设Name 的数据类型是string?)。

最后的演员表也是不必要的。 FirstOrDefault 将返回 Person(实际上是 Person?),因此将其转换为相同的数据类型是一种浪费。

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

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

第 6、7 和 8 行

这些很好。

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

_UID_CUSTOMER = oPerson?.UID_CUSTOMER;

这表示:

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

同样,只有在执行此行之前您不关心 _UID_CUSTOMER 的值时,这才有效。如果你想覆盖_UID_CUSTOMER只要oPerson 不为空时,将其改回 if 语句。


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

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

【讨论】:

  • 感谢您的出色解释!对我来说一清二楚。在 3 小时前的时间里,我将我的代码改造成与您的非常相似的代码,并且我将 DB 和模型更改为下面讨论的感兴趣字段的不可空值。我将复制您的代码以替换我的代码。谢谢。
【解决方案2】:

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

<Nullable>enable</Nullable>

并有这些设置

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-04-18
    • 1970-01-01
    • 1970-01-01
    • 2011-05-27
    • 2015-11-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多