有没有办法在c#中进行不可空类型推断?

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

我终于将一个大项目升级到 .NET Core 和 C# 12,并且很兴奋,因为我现在可以使用可为 null 的引用类型。但我很快发现通过类型推断创建的变量是可以为空的,这违背了可空性检查的全部目的,因为我到处都使用类型推断。

考虑像这样的 API

Task<List<My.Very.Long.Namespace.Chain.Item>> GetSomeDataThatIsNeverNull(int id);

写得真好

var data = await GetSomeDataThatIsNeverNull(123);

写起来很糟糕

List<My.Very.Long.Namespace.Chain.Item> data = await GetSomeDataThatIsNeverNull(123);

只是为了使

data
不可为空。

我理解它以这种方式工作的原因(例如解释herehere),它似乎与稍后分配 null 有关。但这似乎是一个非常具体的用例;大多数时候,我想获取一个对象并使用它,并且永远不会将其设置为 null,如果我这样做,我只想在这种情况下明确它,或者为它创建一个全新的变量。

那么:有没有办法创建一个正确的不可空引用类型,而不必写出类型名称?

更新:评论者要求提供代码示例:

string Foo() => "foo";
var foo = Foo();

Foo()
是一个声明返回
string
的方法,而不是可空的
string
,实际上总是返回非空字符串,但
foo
的类型被推断为
string?
:

输入

string
并不比输入
var
差多少,但对于像
List<My.Very.Long.Namespace.Chain.Item>
这样的类型来说绝对不是这样。

c# type-inference
1个回答
0
投票

与对您问题的一些评论不同,我确实认为您的问题是有效的。换句话说,我相信您希望这段代码生成编译警告:

var foo = "hi";
foo = null;

与此代码生成编译警告的方式相同:

string foo = "hi";
foo = null;

我必须承认,我不确定是否有办法——我不知道有什么办法。然而,虽然我和你的想法一样,控制这个默认值可能会很方便(我也支持更严格而不是更宽松,出于多种原因,推理似乎超出了这里的范围),但我想指出的是我不相信这会“关闭整个功能”,正如您所说。真正的问题是,在你将 x 分配(或不)分配给它之后,你最终会用

null
做什么
?如果您尝试取消引用它 - 编译器将按预期运行。正如预期的那样,这将发出警告:

var foo = "hi";
foo = null;
var bar = foo.Length; // <-- CS8602

这不会像预期的那样:

var foo = "hi";
var bar = foo.Length; // No problem

如果您尝试从方法返回变量,同样的事情。正如预期的那样,这将发出警告:

string MyMethod() {
  var foo = "hi";
  foo = null;
  return foo; // <-- CS8603
}

这不会像预期的那样:

string MyMethod() {
  var foo = "hi";
  return foo; // No problem
}

当然,一个很大的用例是当您的变量不是硬编码值而是从其他 API 表面返回的值时,并且该值仍然按预期运行。正如预期的那样,这将发出警告:

string MyMethod1() {
  var foo = MyMethod2();
  return foo; // <-- CS8603
}

string? MyMethod2() { // Nullable return type
  return "hi";
}

这不会像预期的那样:

string MyMethod1() {
  var foo = MyMethod2();
  return foo; // No problem
}

string MyMethod2() { // Non-nullable return type
  return "hi";
}

注意:如果这在不同的编译目标和语言版本中可能表现不同,我将补充说我的代码是使用 .Net 7.0 和 C# 11 编译的。

因此,静态分析看起来比 IDE 将鼠标悬停在变量上时向我们显示的内容要深入得多。我实际上不知道为什么 Visual Studio(至少从我的版本 17.8.5 开始)不能向我们展示编译器似乎已经知道的相同内容 - 我想这会更清晰,并且几乎会消除(大部分)此讨论的需要。无论哪种方式,也许正确的措辞不是“

var
总是可以为空”,如here所提到的,而是“
var
总是可以推断为可以为空,具体取决于代码的其余部分”。编译器确实允许将
null
分配给变量,但也会了解您是否这样做,并会采取相应的行为。对我来说,这满足了 NRT 承诺的一个重要子集。

如果您发现某些内容无法按预期工作(当然,除了

null
到变量中的纯粹可分配性之外),请对我的答案发表评论,因为我确信社区希望了解这一点.

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