该属性可以永远为 null 还是 VS 是错误的?

Can the property be null ever or is VS wrong?

提问人:Konrad Viltersten 提问时间:6/25/2023 最后编辑:Konrad Viltersten 更新时间:6/25/2023 访问量:80

问:

有了下面的类,我保护它免受 null 的影响。Values

public class Container
{
  public Guid Id { get; init; }
  public virtual IEnumerable<Measure> Measures { get; init; }
  public Dictionary<string, Measure[]> Measurement 
    => (Measures ?? Array.Empty<Measure>())
      .GroupBy(a => a.Type).ToDictionary(a => a.Key, a => a.ToArray());
}

public class Measure
{
  public string Name { get; set; }
  public string Type { get; set; }
}

出于某种原因,我的 IDE 将合并标记为多余,声称该属性永远不会为 null(即 可以重构为 )。Measures??Array.Empty<Measure>()Measures

public class Container
{
  public Guid Id { get; init; }
  public virtual IEnumerable<Measure> Measures { get; init; }
  public Dictionary<string, Measure[]> Measurement 
    => Measures
      .GroupBy(a => a.Type).ToDictionary(a => a.Key, a => a.ToArray());
}

当我听从这个建议时,一位同事指出它是有缺陷的。我同意他的观点,但这是 VS/R# 中的一个错误,这在很大程度上是一个现实的解释。更现实的是:是我们错了。因此,我感觉到我们都遗漏了一些东西,并且IDE在某种程度上是正确的。

enter image description here

不过,我无法解释是怎么回事。

我尝试的是删除、添加和其他一些更改。无论如何,在小提琴中敲击它,如果合并被移除,就会抛出。virtual= default!ArgumentNullException

c# null null 合并运算符

评论

0赞 Dai 6/25/2023
不要使用 .使用适当的构造函数。 令人惊讶的是,合法的用例很少。initinit
0赞 Dai 6/25/2023
永远不要使用或两者(我知道 EF 示例代码将其用于成员,但除此之外,从来没有一个很好的理由这样做,即使那样,我觉得 EF 示例是错误的,但给人们愚蠢的快速修复以保持示例更短)。= default!= null!DbSet
0赞 shingo 6/25/2023
但 dotnetfiddle 并没有将合并标记为多余,视觉工作室也没有。也许您的 IDE 认为永远不会为空,但会。IEnumerable<Measure>IEnumerable<Measure>?
0赞 Konrad Viltersten 6/25/2023
@shingo 它可以是 VS 或 R#(即 Resharper)。在我看来,两者都是可靠的,而且可能比我“更聪明”。通常。请参阅编辑。至于 - 我认为默认情况下可以为 null,并且不需要显式的可为 null 性标记。IEnumerable<T>?
0赞 Konrad Viltersten 6/25/2023
@Dai 有趣的建议。那么,推荐的语法究竟是什么呢?EF 需要一个空构造函数(我可以在其中分配给虚拟属性)。但是,EF 使用初始值设定项设置值,因此我不能仅使用。因此,我会着手使值可变。这难道不令人不快或至少不气馁吗?{get;}{get;set;}

答:

-1赞 rotabor 6/25/2023 #1

它看起来与“Nullable”项目设置非常相关:enter image description here

更新日期无论“Nullable”设置如何,我都无法重现 IDE 的消息。

Microsoft Visual Studio Community 2022(64 位)- 当前版本 版本 17.6.0

使用“Nullbale”是“启用”:

1>------ 构建开始:项目:WinFormsApp3,配置:调试任何 CPU ------

1>C:\Users\TMSAdmin\source\WinFormsApp3\WinFormsApp3\Form1.cs(99,23,99,27):警告 CS8618:退出构造函数时,不可为 null 的属性“Name”必须包含非 null 值。请考虑将该属性声明为可为 null。

1>C:\Users\TMSAdmin\source\WinFormsApp3\WinFormsApp3\Form1.cs(100,23,100,27):警告 CS8618:退出构造函数时,不可为 null 的属性“Type”必须包含非 null 值。请考虑将该属性声明为可为 null。

1>C:\Users\TMSAdmin\source\WinFormsApp3\WinFormsApp3\Form1.cs(92,45,92,53):警告 CS8618:退出构造函数时,不可为 null 的属性“度量值”必须包含非 null 值。请考虑将该属性声明为可为 null。

1>WinFormsApp3 -> C:\Users\TMSAdmin\source\WinFormsApp3\WinFormsApp3\bin\Debug\net6.0-windows\WinFormsApp3.dll

1>完成构建项目“WinFormsApp3.csproj”。

========== 构建:1 次成功,0 次失败,0 次最新,0 次跳过 ==========

评论

0赞 Konrad Viltersten 6/25/2023
在 CSPROJ 中显示结果行可能会更容易。设置的 GUI 可能因 IDE 而异。至于消息,它可能是由 R# 提供的,而不是 VS 本身提供的。不过,两者都非常可靠,所以我相信它有一些东西。除非另有确认,否则我的问题。(注意。不是我的反对票。
3赞 shingo 6/25/2023 #2

NRT文件提到:

未追加到类型名称的任何变量都是不可为 null 的引用类型。这包括启用此功能后现有代码中的所有引用类型变量。?

因此,如果在项目上启用了 nullable,则根据此标准,分析器应假定该标准永远不会为 null。Measures

仅供参考,如果您尚未初始化该属性,您应该会看到 CS8618 警告,但您可以忽略它。如果要避免忽略此警告,可以在项目设置中的“将特定警告视为错误”中添加“可为空”,以报告编译错误。Measures

评论

0赞 Dai 6/25/2023
不幸的是,编译器做出了这个假设,尽管由于使用了 .init
0赞 shingo 6/25/2023
它不应该由于使用 .我测试了它,即使我使用或仅使用,VS 仍然会告诉我“'度量值'在这里不是空”。initsetget
0赞 Dai 6/25/2023
这不是我的意思 - 我的意思是 C# 不会警告 的属性不能保证始终被设置(即,如果在构造时从未设置该属性,则如此)。class ContainerMeasuresnullinit
0赞 Konrad Viltersten 6/25/2023
@Dai我感觉到,这个可空性概念可能没有被打破,但目前支持得有点差。就像他们想做一样,并且中途完成了。然后,发布窗口来了,PL告诉他们要跟着它走。还是我在这里错过了更大的图景?
0赞 Dai 6/25/2023
@KonradViltersten 当他们在 2019 年左右在 .NET Core 3.1 中引入它时,是的 - 但从那时起(以及从 C# 10.0 开始),不可为 null 属性的“解决方法”实际上是由于从未设置过设置而添加修饰符(或仅使用构造函数......initnullrequired