为啥在 C# 10 中我会在初始化属性上收到编译器警告/错误 CS8618

Posted

技术标签:

【中文标题】为啥在 C# 10 中我会在初始化属性上收到编译器警告/错误 CS8618【英文标题】:Why in C# 10 do I get a compiler warning/error CS8618 on init properties为什么在 C# 10 中我会在初始化属性上收到编译器警告/错误 CS8618 【发布时间】:2022-01-20 22:53:43 【问题描述】:

我有这个代码

#enable nullable
public class SomeClass

  public string SomeProperty  get; init; 

出现以下编译错误:

[CS8618] 退出构造函数时,不可为空的属性“SomeProperty”必须包含非空值。考虑将属性声明为可为空。

我认为将属性声明为 init 的全部意义在于确保在创建时使用属性初始化器语法设置属性,例如

var s = new SomeClass  SomeProperty = "some value" ;

那么为什么我会收到这个编译器警告呢?我是不是完全误解了init 的用途?

编辑 1:

我不是唯一一个被这个绊倒的人。有一个Roslyn issue 几乎是同一件事。

还有一个关于required properties的提案,其中Richiban asks:

init 道具不是强制性的原因是什么?我可能有 错过了一些东西,但我想他们会很少使用 没有req 修饰符。

“可以初始化”和“必须初始化”之间存在区别,init 关键字无法清楚地传达这一点。由于围绕不可空引用类型和正确代码的所有模糊,我错误地认为init 意味着“必须初始化”。

【问题讨论】:

【参考方案1】:

仅初始化属性不要求在创建时使用属性初始值设定项语法设置属性。它允许以这种方式设置它们,而不是要求所有只读属性都由构造函数设置。

所以用你的代码,

var s = new SomeClass();

... 仍然是完全有效的,并且会以 s.SomeProperty 为空,与其类型的可空性相反。

【讨论】:

我认为 init 是为那个用例设计的。所以编译器会抱怨缺少初始化。因为对于对象消费者来说,创建和初始化之后的状态才是最重要的。您的示例不应是有效代码。我很失望。 @ThomasEyde 即使new SomeClass(); 无效,在泛型类中您仍然会遇到new T(); 的相同问题。如果要确保调用者设置SomeProperty,我会在构造函数中添加一个参数。【参考方案2】:

带有init关键字的属性值只能在构造函数或初始化块中改变。

如果你使用默认构造函数

var s = new SomeClass ();
s.SomeProperty = "A value";//Compiler error CS8852  Init-only property or indexer "SomeClass.SomeProperty" can only be assigned in an object initializer, or on "this" or "base" in an instance constructor or an "init" accessor.

属性 SomeProperty 被初始化为 null,但它是 not nullable 引用类型。编译器正在报告未来可能出现的运行时错误。 警告 CS8618 退出构造函数时,不可为空的属性“SomeProperty”必须包含非空值。考虑将属性声明为可为空。

如果发生这种情况没有问题,可以使用以下命令删除警告

1.使用null forgiving operator

public class SomeClass

    public string SomeProperty  get; init;  = null!;// indicating null value is a correct not null value. 

当您尝试访问和使用 SomeProperty 值时,您需要检查可空性以避免空引用异常,即使该类型指示它不可为空。这很烦人。

if(s.SomeProperty==null) //OK

   //DO SOME STUFF


var length = s.SomeProperty?.Length ?? 0; //OK

var length2 = s.SomeProperty;//NullReferenceException

2。分配/初始化属性的有效值

public class SomeClass

    public string SomeProperty  get; init;  = "My initial value"; 

您可以在不初始化属性的情况下调用默认构造函数var s = new SomeClass (); //Some property is "My initial value"

或使用初始化块调用构造函数

var s = new SomeClass()

   SomeProperty = "My new intial value"
;


var length = s.SomeProperty.Length; //NO exception / No problem

3.创建带参数的构造函数,避免使用默认构造函数。

错误/警告仅出现在调用者代码中。

public class SomeClass

    public string SomeProperty  get; init;  //ALL OK. CS8618 dissapeared / No Warning.

    public SomeClass(string someProperty)
    
        SomeProperty = someProperty;
    


var s = new SomeClass(); //CS7036 ERROR.


var s2 = new SomeClass() //CS7036 ERROR.

    SomeProperty = "My new intial value"
;

var s3 = new SomeClass("My new initial value"); //OK. No Warning

var s4 = new SomeClass("My new initial value") //OK. No Warning.

    SomeProperty = "My other new intial value"
;

s4.SomeProperty = "A value"; //CS8852 ERROR. SomeProperty is enabled to only be initialized in constructor or intialize block.

【讨论】:

我的问题的精神是为什么 init 不处理不可为空的问题。 Jon Skeet 解释说 init 不是这样工作的。感谢您就如何避免编译器错误提出的建议,但这绝不是我的本意。我想要这些错误,但我想要它们在适当的时间出现,这对我来说是在初始化之后。此外,仅在存在合理的默认值时才提供默认值。就我而言,没有。编译器有效值不一定与域有效值相同。 检查更新,第三种形式。创建类实例时出现错误。

以上是关于为啥在 C# 10 中我会在初始化属性上收到编译器警告/错误 CS8618的主要内容,如果未能解决你的问题,请参考以下文章

为啥我会收到此错误? 'Observable<boolean>' 类型上不存在属性 'map'

为啥我会收到“通过指定其 SameSite 属性指示是不是在跨站点请求中发送 cookie”?

为啥我会收到此错误:未捕获的类型错误:无法读取 null 的属性 'classList'

为啥我会收到这些错误,java truetype 错误?

为啥我会在本机代码中收到此 UnsatisfiedLinkError?

为啥我会收到 ORA-06531:对未初始化集合的引用?