TL;DR个
C#编译器不会为 struct 生成默认的ctor,因此如果有未声明的-字段初始化器将不会运行,因此它们基本上没有意义,为了防止这种意外行为,编译器不允许在没有声明ctor的情况下使用字段初始化器(不一定是默认的).
The longer rant:个
首先,正如this answer(在编辑部分)所解释的那样:
无参数构造函数不是由编译器创建的.就CLR而言,值类型不一定要有构造函数--尽管事实证明,如果您用IL编写它,它就可以.当您用C#编写new Guid()
时,它会发出与调用普通构造函数时不同的IL.
即the following:
Console.WriteLine(typeof(Struct).GetConstructors().Length); // prints 0
Console.WriteLine(typeof(Class).GetConstructors().Length); // prints 1
struct Struct
{
}
class Class
{
}
(请注意,对于struct Struct{ public Struct() { } }
,上面的代码将打印1)
因此,其中一个原因可能是性能和向后兼容性.
此外,还有一些关于这LDM个主题的注释:
我们觉得这里的一条简单规则是用引用类型映射观察到的行为:如果 struct 类型没有显式构造函数,我们将合成该构造函数.如果构造函数碰巧为空(就像今天在无构造函数 struct 类型中一样,因为字段初始值设定项还不受支持),我们就不再优化该构造函数.如果显式声明了构造函数,我们将不会合成该类型的任何构造函数,并且除非也显式声明了无参数构造函数,否则new S()
将不会运行字段初始值设定项.这确实有一个潜在的失败trap ,用户希望无参数构造函数运行字段初始值设定项,但合成一个无参数构造函数会对具有主构造函数的记录 struct 产生不良的连锁react :无参数构造函数会在那里做什么?它没有任何可以用来调用主构造函数的东西,并且会导致语义混乱.
这导致drat specification人中增加了以下几个:
如果 struct 具有字段初始值设定项而没有声明的实例构造函数,则会报告错误,因为字段初始值设定项将不会运行.
和
如果未声明无参数实例构造函数,则 struct (请参见§15.4.9)...
隐式具有一个无参数的实例构造函数,该构造函数始终返回将所有值类型字段设置为其缺省值并将所有引用类型字段设置为空的值.
结果是这quite noteworthy behaviour个:
Console.WriteLine(new V().j); // prints 8
Console.WriteLine(new V1().j); // prints 0 - no custom parameterless ctor, no init
struct V
{
public V() { }
public V(int _) {}
public int j = 8;
}
struct V1
{
public V1(int _) { }
public int j = 8;
}
P.S.个
请注意,似乎至少有一些.NET 6 SDK版本不能正确地违反上面的规范,即:
Console.WriteLine(new V().j);
struct V
{
public int j = 8;
}
不仅是compiles but prints 8:
尽管这个问题似乎已经在最新的(在 compose 本文时)6.0.400SDK中得到了修复.