为什么我不能在初始化器中初始化只读变量?以下代码并没有按照预期运行:
class Foo
{
public readonly int bar;
}
new Foo { bar=0; }; // does not work
这是CLR的一些技术限制导致的吗?
编辑
我知道new Foo { bar=0; }
和new Foo().bar=0;
是相同的,但是“只读”是由CLR强制执行的,还是仅仅是编译器的限制?
为什么我不能在初始化器中初始化只读变量?以下代码并没有按照预期运行:
class Foo
{
public readonly int bar;
}
new Foo { bar=0; }; // does not work
这是CLR的一些技术限制导致的吗?
编辑
我知道new Foo { bar=0; }
和new Foo().bar=0;
是相同的,但是“只读”是由CLR强制执行的,还是仅仅是编译器的限制?
这个初始化器只是一种语法糖。当你写下:
new Foo { bar=0; };
(顺便说一下,这是语法错误,应该是这样的...)
new Foo { bar=0 }
实际上发生的是:
var x = new Foo();
x.bar = 0;
由于该属性是只读的,所以第二个语句是无效的。
编辑:根据你的编辑,问题有点不清楚。一个readonly
属性是设计为不可设置的。它在对象构造时构建。这由编译器和运行时都强制执行。(尽管我没有测试过后者,因为需要一些技巧才能绕过前者。)
要记住,有两个“编译”阶段。将C#代码编译成IL代码时强制执行,将IL代码编译成机器代码时也强制执行。
这不是CLR的技术限制,考虑到明确的readonly
声明,它正按照其预期方式工作。在对象构造后,您无法设置readonly
属性。
Foo
的作者并没有预料到你会这样做。但也许提问者对此感兴趣,作为对你出色回答的评论。 - Jeppe Stig Nielsen在初始化程序中设置readonly
会引入无法在编译时强制执行的矛盾和复杂性。我想限制是为了避免歧义。关键是进行编译时验证。
想象一下:
class Foo
{
public readonly int bar;
Foo () {
// compiler can ensure that bar is set in an invoked ctor
bar = 0;
}
}
// compiler COULD know that `bar` was set in ctor
// and therefore this is invalid
new Foo { bar = 0; }
class Foo
{
public readonly int bar;
Foo () {
// imagine case where bar not set in ctor
}
}
// compiler COULD know that `bar` is not bound yet
// therefore, this COULD be valid
new Foo { bar = 0; }
// but this COULD be proved to never be valid
new Foo();
T G<T> () where T : new
{
// What in heck should happen *at compile time*?
// (Consider both cases above.)
// What happens if T (Foo) changes to include/not-include setting the
// readonly variable in the ctor?
// Consider intermediate code that invokes G<Foo>() and this other
// code is NOT recompiled even though Foo is--
// Yet a binary incompatibility has been added!
// No thanks!
return new T();
}
G<Foo>();
我认为我所概述的案例展示了使用“动态”readonly
方法的一些复杂性,最终我相信这只是一种“选择性语言限制”(编译器实现语言)来强制/允许编译时验证。
https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/proposals/csharp-9.0/init
struct Point
{
public int X { get; init; }
public int Y { get; init; }
}
var p = new Point() { X = 42, Y = 13 };
readonly
_fields_,它们现在是_properties_,生成的IL现在将是一个函数调用而不是访问字段。请参见https://sharplab.io/#v2:C4LglgNgNAJiDUAfAAgZgAQCcCmBDGA9gHYQCe6AzsJgK4DGw6ASnjAPImkBiY2EMAWABQAb2HoJ6NFlbEy6MEUYA1XBBrYA3OMk6J0lvg5kefGAApFjAG5qNASj3oxQyW/Sr12dAF50tr21XSQBfYTChYWkqWgZmVmNSAAVMAgAHbExgUmEXN2krDztvEXQAc2xgTQUiMCr0CIiojGQAJnQAFWwqXKcCpXj8U35zR2CJPPcJW0waqlwiOm8/ImwAd0H2TmGLABZW+yCp6dxZgI1fOeAFpYA6Tw0j4+QAdn9ip4kItz6MQsMYCl0plsqMnJMpjMrjdluhVhsAYkgRksuRSg9YfsGp93FDzrDFPNFth7h8nPk3vicY1hEA=== - Erunehtarreadonly
变量必须在构造函数中初始化,而属性初始化程序在对象构造之后执行,因此不可行。
因为初始化器等同于
var foo = new Foo();
foo.bar=0;
class Foo
{
public readonly int bar;
Foo(int b)
{
bar = b; // readonly assignments only in constructor
}
}
Foo x = new Foo(0);
因为您指定了它为只读。这样指定某物为只读,然后期望写入语句起作用是没有意义的。
readonly关键字是一个修饰符,可用于字段上。当字段声明包括readonly修饰符时,只能在声明中或同一类中的构造函数中作为声明引入的字段进行赋值。
我知道这不是对发帖者问题的直接回答,但是C#的新版本现在允许从属性本身进行直接初始化,如下所示。
public int bar { get; set; } = 0;
再次声明,我知道这并没有解决上述已经识别(和解决)的只读问题。