为什么 Visual Studio 将新创建的数组键入为 Nullable?
Why does Visual Studio Type a Newly Minted Array as Nullable?
我正在编写一个具有通用类型的函数 TVal
。我写了这一行:
var zeroBased = new TVal[size];
然后在 Visual Studio (VS) 中,我使用 alt+enter 将 var
替换为显式类型。这是我得到的:
TVal[]? zeroBased = new TVal[size];
我很惊讶地发现 ?
运算符,表明该类型可能是可为空的。我认为假设使用 new
创建时类型永远不会为 null 就足够安全了,并且可以刚刚完成:
TVal[] zeroBased = new TVal[size];
是否存在在 C# 中实例化新数组可以返回 null 的场景?
注意:没有 ?
,代码似乎编译得很好,我只是对 VS 的建议很感兴趣...
最小可验证示例
打开 Visual Studio,与下面指定的版本相同,创建一个新项目,根据下面的 VS 项目文件内容启用可空类型,创建一个新的 class,然后弹出此函数:
public void Test<T>(int size)
{
var tArr = new T[size];
}
select var
并点击 alt+enter
,并选择用显式类型替换 var
。如果行为与我所经历的相同,您将得到:
public void Test<T>(int size)
{
T[]? tArr = new T[size];
}
Visual Studio 项目文件内容
我们在该项目中使用 C# 8,并且启用了 Nullables:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Nullable>enable</Nullable>
<LangVersion>8.0</LangVersion>
<WarningsAsErrors>CS8600;CS8602;CS8603</WarningsAsErrors>
<TargetFramework>netstandard2.0</TargetFramework>
<OutputType>Library</OutputType>
<Version>1.0.0.9</Version>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
<PackageReference Include="System.Dynamic.Runtime" Version="4.3.0" />
</ItemGroup>
</Project>
Visual Studio 版本信息(只是对这个问题很重要的部分)
Microsoft Visual Studio 社区 2019
版本 16.6.1
VisualStudio.16.Release/16.6.1+30128.74
微软.NET框架
版本 4.7.03062
已安装版本:社区
C# 工具 3.6.0-4.20251.5+910223b64f108fcf039012e0849befb46ace6e66
IDE 中使用的 C# 组件。根据您的项目类型和设置,可能会使用不同版本的编译器。
Visual Studio 与 C# 8 允许您根据您在项目中设置的上下文使用可空类型。您可以找到文档 Here.
启用它的一种方法是在项目文件中使用 <Nullable>enable</Nullable>
条目。如果你有,那么当你转换为显式变量时它会选择使用可空类型。
我不确定相同的行为是否会用于其他方式(例如编译指示)以启用它。我只试了项目文件的方法。
我想通过添加一些链接来扩展现有答案
C# specification proposal says:
nullable implicitly typed local variables
var
infers an annotated type for reference types. For instance, in
var s = ""
; the var
is inferred as string?
.
表示引用类型的var
推断出可为空的引用类型。如果使用项目文件或 #nullable
pragma.
启用 nullable context
,则此方法有效
此行为已被讨论 in this LDM and implemented in this issue。
This is a reason 用于使 var
推断出可为空的引用类型:
At this point we've seen a large amount of code that requires people
spell out the type instead of using var, because code may assign null
later.
我想用我自己的解释来扩展现有答案。 破解了这个问题并直达核心。这一切都归结为启用了可空性——我们为这个项目(但不是其他人,这让我失望了!)。
then added some explicit references in support of the original answer. In particular, we were pointed to a recent discussion by the C# team on Github。该文件和现有答案引用了以下内容:
At this point we've seen a large amount of code that requires people spell out the type instead of using var, because code may assign null later.
如果没有上下文,我发现这很难理解。所以这里是解释上述内容的上下文:
var current = myLinkedList.Head; // annotated not null
while (current is object)
{
...
current = current.Next; // warning, Next is annotated nullable, but current is non-null
}
分解一下,让我们看看第一行:
var current = myLinkedList.Head; // annotated not null
由于 Head
属性 被注释为非空,编译器将 var 解释为不可空是完全可以的。然而,这种不可为空性将永远与变量保持一致,即使在程序中的某个时刻,我们想让它为空,例如在这一行中:
current = current.Next; // warning, Next is annotated nullable, but current is non-null
C# 团队说,好的,我们这里有两个选择。我们可以将 var
解释为始终可为空,或者我们可以将其解释为不可为空(当它可从上下文推断时),并允许用户指定 var?
以明确声明他们想要一个可为空的类型。但是我阅读他们的文档是 var?
有点违反 var
的整个原则,这是方便的。如果我们每次使用它时都必须在 var
的末尾附加一个额外的 ?
,那么可能是时候明确类型并停止使用 var.
因此,C#团队得出结论:
Make var have a nullable annotated type and infer the flow type as normal.
这意味着,如果您将不可为 null 的对象分配给 var
,您可以稍后在代码中安全地将 null 分配给同一引用,而不会收到任何警告。 Hans 也对此发表了评论。因此,例如,将其恢复为原始 Q,我可以这样做:
public void Test<T>(int size)
{
var tArr = new T[size];
//Some code
tArr = null; //This is OK, because var is treated as T[]?, not T[]
}
而且我不会收到任何警告。所以 VS 在这里表现得很好——它尊重编译器将 var
视为可空的行为,正如设计的那样, 即使该 var 被初始化为不可空值 .意思是,这对我来说是关键点,也是我问题的核心:
作为程序员,在将 var 转换为显式类型后是否删除可空性取决于您的需要。
这就是我在这种情况下要做的!
我正在编写一个具有通用类型的函数 TVal
。我写了这一行:
var zeroBased = new TVal[size];
然后在 Visual Studio (VS) 中,我使用 alt+enter 将 var
替换为显式类型。这是我得到的:
TVal[]? zeroBased = new TVal[size];
我很惊讶地发现 ?
运算符,表明该类型可能是可为空的。我认为假设使用 new
创建时类型永远不会为 null 就足够安全了,并且可以刚刚完成:
TVal[] zeroBased = new TVal[size];
是否存在在 C# 中实例化新数组可以返回 null 的场景?
注意:没有 ?
,代码似乎编译得很好,我只是对 VS 的建议很感兴趣...
最小可验证示例
打开 Visual Studio,与下面指定的版本相同,创建一个新项目,根据下面的 VS 项目文件内容启用可空类型,创建一个新的 class,然后弹出此函数:
public void Test<T>(int size)
{
var tArr = new T[size];
}
select var
并点击 alt+enter
,并选择用显式类型替换 var
。如果行为与我所经历的相同,您将得到:
public void Test<T>(int size)
{
T[]? tArr = new T[size];
}
Visual Studio 项目文件内容
我们在该项目中使用 C# 8,并且启用了 Nullables:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Nullable>enable</Nullable>
<LangVersion>8.0</LangVersion>
<WarningsAsErrors>CS8600;CS8602;CS8603</WarningsAsErrors>
<TargetFramework>netstandard2.0</TargetFramework>
<OutputType>Library</OutputType>
<Version>1.0.0.9</Version>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
<PackageReference Include="System.Dynamic.Runtime" Version="4.3.0" />
</ItemGroup>
</Project>
Visual Studio 版本信息(只是对这个问题很重要的部分)
Microsoft Visual Studio 社区 2019 版本 16.6.1 VisualStudio.16.Release/16.6.1+30128.74 微软.NET框架 版本 4.7.03062
已安装版本:社区
C# 工具 3.6.0-4.20251.5+910223b64f108fcf039012e0849befb46ace6e66 IDE 中使用的 C# 组件。根据您的项目类型和设置,可能会使用不同版本的编译器。
Visual Studio 与 C# 8 允许您根据您在项目中设置的上下文使用可空类型。您可以找到文档 Here.
启用它的一种方法是在项目文件中使用 <Nullable>enable</Nullable>
条目。如果你有,那么当你转换为显式变量时它会选择使用可空类型。
我不确定相同的行为是否会用于其他方式(例如编译指示)以启用它。我只试了项目文件的方法。
我想通过添加一些链接来扩展现有答案
C# specification proposal says:
nullable implicitly typed local variables
var
infers an annotated type for reference types. For instance, invar s = ""
; thevar
is inferred asstring?
.
表示引用类型的var
推断出可为空的引用类型。如果使用项目文件或 #nullable
pragma.
nullable context
,则此方法有效
此行为已被讨论 in this LDM and implemented in this issue。
This is a reason 用于使 var
推断出可为空的引用类型:
At this point we've seen a large amount of code that requires people spell out the type instead of using var, because code may assign null later.
我想用我自己的解释来扩展现有答案。
At this point we've seen a large amount of code that requires people spell out the type instead of using var, because code may assign null later.
如果没有上下文,我发现这很难理解。所以这里是解释上述内容的上下文:
var current = myLinkedList.Head; // annotated not null
while (current is object)
{
...
current = current.Next; // warning, Next is annotated nullable, but current is non-null
}
分解一下,让我们看看第一行:
var current = myLinkedList.Head; // annotated not null
由于 Head
属性 被注释为非空,编译器将 var 解释为不可空是完全可以的。然而,这种不可为空性将永远与变量保持一致,即使在程序中的某个时刻,我们想让它为空,例如在这一行中:
current = current.Next; // warning, Next is annotated nullable, but current is non-null
C# 团队说,好的,我们这里有两个选择。我们可以将 var
解释为始终可为空,或者我们可以将其解释为不可为空(当它可从上下文推断时),并允许用户指定 var?
以明确声明他们想要一个可为空的类型。但是我阅读他们的文档是 var?
有点违反 var
的整个原则,这是方便的。如果我们每次使用它时都必须在 var
的末尾附加一个额外的 ?
,那么可能是时候明确类型并停止使用 var.
因此,C#团队得出结论:
Make var have a nullable annotated type and infer the flow type as normal.
这意味着,如果您将不可为 null 的对象分配给 var
,您可以稍后在代码中安全地将 null 分配给同一引用,而不会收到任何警告。 Hans 也对此发表了评论。因此,例如,将其恢复为原始 Q,我可以这样做:
public void Test<T>(int size)
{
var tArr = new T[size];
//Some code
tArr = null; //This is OK, because var is treated as T[]?, not T[]
}
而且我不会收到任何警告。所以 VS 在这里表现得很好——它尊重编译器将 var
视为可空的行为,正如设计的那样, 即使该 var 被初始化为不可空值 .意思是,这对我来说是关键点,也是我问题的核心:
作为程序员,在将 var 转换为显式类型后是否删除可空性取决于您的需要。
这就是我在这种情况下要做的!