编译器生成代码中属性为何不用同名后备字段?CompilerGenerated属性触发条件
问题解答
为什么用string_1而非FileName作为属性的后备字段?
- 防止递归死循环:如果在
FileName属性的get访问器里直接写return FileName,会无限调用自己的get方法,直接导致栈溢出。所以编译器必须用一个和属性名不同的名字来做后备字段,避免这种递归调用。 - 区分自动生成与手写代码:编译器生成的成员会用这种“前缀+数字”的非常规命名,既能和你自己写的代码成员划清界限,也能避免不小心和你手动定义的变量重名。
编译器什么时候会生成代码?哪些情况会触发[CompilerGenerated]属性?
编译器会在很多场景下自动生成代码并打上这个属性标记,常见的有这些情况:
- 自动属性:当你写
public string FileName { get; private set; }这种不带手动后备字段的自动属性时,编译器就会偷偷生成像string_1这样的私有后备字段,同时给get/set方法加上[CompilerGenerated]。 - 匿名类型:比如写
var user = new { Name = "张三", Age = 30 },编译器会自动生成对应的类、属性和后备字段,这些自动生成的东西都会带这个属性。 - Lambda表达式:把Lambda赋值给委托或者用在LINQ里时,编译器会把它转成匿名方法或者生成对应的类方法,这些生成的成员会被标记。
- 迭代器方法:用
yield return写的迭代器,编译器会生成一个状态机类来处理迭代逻辑,这个类和它的成员都会带[CompilerGenerated]。 - 异步方法:带
async/await的异步方法,编译器会生成异步状态机类,相关的自动生成代码都会被标记。 - 接口默认方法:C#8及以后,接口里写的默认实现方法,编译器会自动生成对应的实现代码,打上这个属性。
- 记录类型(Record):C#9及以后的record类型,编译器会自动生成Equals、GetHashCode、ToString这些方法,还有对应的后备字段,这些生成的内容都会带该属性。
- 元组类型:比如
(string Name, int Age)这种元组,底层是编译器生成的类,相关成员也会被标记。
内容的提问来源于stack exchange,提问作者li yixiao
相关产品推荐
相关产品推荐

