为什么 LINQ SQL 语法不考虑可空性推断?

问题描述 投票:0回答:2

考虑这两种方法:

IEnumerable<T> WhereNotNull<T>(IEnumerable<T?> seq) =>
  from item in seq
  select item;

IEnumerable<T> WhereNotNull2<T>(IEnumerable<T?> seq) =>
  seq.Select(item => item);

第一种方法,使用 SQL LINQ 语法,不会引发任何编译器警告,而第二种方法,使用方法链,会:

Nullability of reference types in value of type 'System.Collections.Generic.IEnumerable<T?>' does not match target type 'System.Collections.Generic.IEnumerable<T>'

为什么在第一种方法中丢失了可空类型信息?

c# linq nullable
2个回答
1
投票

编译器使用静态分析来确定变量的空状态。变量要么不为空,要么可能为空。编译器通过两种方式确定变量不为空:

  1. 已为变量分配了一个已知不为空的值。
  2. 变量已针对 null 进行检查,并且自该检查后未被修改。 这是来自microsoft

所以对于第一个函数

WhereNotNull
编译器没有发现变量
seq
有什么奇怪的,因为此时它不是静态求值的。代码

  from item in seq
  select item;

将进一步转换为流利的语法

seq.Select(n=> n)
.

WhereNotNull2
的第二个示例中,编译器可以应用空状态分析并检测 maybe-null 情况并触发
seq
可能为空的警告。


0
投票

可空类型信息在第一种方法中并没有丢失,但是 SQL LINQ 语法的 from 子句中的代码自动从序列中过滤掉任何空值,有效地将 IEnumerable 序列转换为 IEnumerable 序列。

另一方面,代码的第二个方法链接版本不会从序列中过滤掉任何空值,因此生成的序列仍然包含类型 T? 的可为空元素。

要使第二种方法有效,您可以添加一个 Where 子句来过滤掉空值,如下所示:

IEnumerable<T> WhereNotNull2(IEnumerable<T?> seq) =>
seq.Where(item => item.HasValue).Select(item => item.Value);

这将在将非空值投影到 IEnumerable 序列之前从序列中过滤掉任何空值。

© www.soinside.com 2019 - 2024. All rights reserved.