VS 2022 17.1 不会构建 C# 10 结构 (CS8983)。 17.0.5 喜欢它。发生了什么?

VS 2022 17.1 wont build C# 10 struct (CS8983). 17.0.5 liked it. What happened?

直到昨晚我从版本 17.0.5 升级到 17.1 之前,我一直在 VS2022 中构建这个 C# 结构

internal struct RoutineSettings
{
    public bool Show { get; set; } = true;
    public ShapeType PreferredShapeType { get; set; } = ShapeType.None;
}

ShapeType 只是一个枚举)。

升级后,出现此错误:

error CS8983: A 'struct' with field initializers must include an explicitly declared constructor.

解释很简单。但是由于这段代码在我感到困惑之前执行得很好。

  1. 是不是编译器出错了?
  2. 这是一个真正的语言要求,编译器直到现在才检查失败吗?

如果正确答案是#2,它会提出问题为什么这是一项要求?为什么需要它?因为当我“修复”它时,修复似乎毫无意义。我刚刚添加了一个空的默认构造函数:

internal struct RoutineSettings
{
    public RoutineSettings() { }
    public bool Show { get; set; } = true;
    public ShapeType PreferredShapeType { get; set; } = ShapeType.None;
}

现在我的代码再次构建。当它什么都不做时我为什么需要它?

这似乎确实是以前版本中的编译器错误。在 C# 9.0 之前,结构不允许有默认构造函数,而且它实际上没有生成,这与 类 不同,在没有定义构造函数时自动生成默认构造函数。这允许在不调用代码的情况下创建值类型(仅通过使内存无效)。很明显,他们想明确说明这一变化,因为无论默认构造函数是否存在,它都会对生成的代码产生影响。请注意,在您的示例中,默认构造函数不为空,因为任何字段初始值设定项都隐式添加到构造函数代码中。所以在C#10中,声明构造函数的时候,就生成了,否则从生成的IL中离开。

另请注意,使用此功能时会遇到很多陷阱。分配结构数组时仍未执行默认构造函数:

        [Fact]
        public void StructCtorIsExecuted()
        {
            var r = new RoutineSettingsWithDefaultCtor();
            Assert.Equal(10, r.PreferredShapeType);

            RoutineSettingsWithDefaultCtor[] array = new RoutineSettingsWithDefaultCtor[10];
            Assert.Equal(0, array[0].PreferredShapeType); // <-- When allocating an array, the default ctor is NOT executed
        }

        internal struct RoutineSettingsWithDefaultCtor
        {
            public RoutineSettingsWithDefaultCtor()
            {
                PreferredShapeType = 10;
            }

            public bool Show { get; set; } = true;
            public int PreferredShapeType { get; set; } = 2;
        }
}

这里描述了更多问题:https://davidshergilashvili.space/2021/09/05/c-10-struct-type-can-define-default-constructor/

这不是错误,而是 C# 的新设计选择:

https://github.com/dotnet/sdk/issues/23971

解法:

定义一个 public parameter-less 构造函数,它将解决问题。

例如,之前:

public struct Abc
{
     public int Num1 = 5;
}

之后:

public struct Abc
{
     public int Num1 = 5;
     public Abc() {} // <--- Parameter-less default constructor
}