可以看到,内插字符串表达式 $"{age}1001A" 里用到了 age 参数值。假设这段代码是 Student 里的全部内容的话,那么这个类型似乎是没有定义任何成员的,此时的 age 照着我们理解的逻辑来看,无法以任何形式传到属性里去处理。编译器也是这么认为的,因此编译器会针对于这种情况,生成一个底层字段,然后这里的 age 用于内插字符串时,就是调用的这个字段。这就会造成隐式行为:字段的生成。
第一个问题是,record 和 record struct 类型里的这种构造器语法形式,在等价到 class 和 struct 上时,底层本身就自带一个自动属性的声明,因此他们的标识符命名格式就成了不一致的地方。在普通数据类型(这里只指 class 和 struct,而 interface、enum 和 delegate 都不能有主构造器)里,因为是构造器参数,因此遵循驼峰命名法;而对于 record 类型来说,则因为底层带有一个帕斯卡命名法的自动属性,因此定义的时候,即使也是构造器参数,但这里用的是帕斯卡命名法,主要是为了契合底层属性的书写风格。
class Student(string? name, int age, Gender gender)
{
public string Name { get; } = name ?? throw new ArgumentNullException(nameof(name));
public int Age { get; } = age;
public Gender Gender { get; } = gender;
}
class Student(string? name, int age, Gender gender)
{
public string Name { get; } = name ?? throw new ArgumentNullException(nameof(name));
public int Age { get; } = ++age; // 这里
public Gender Gender { get; } = gender;
}
class Student(string? name, int age, Gender gender)
{
public Student(string? name, Gender gender) // 这里
{
}
}
class Student(string? name, int age, Gender gender)
{
public Student(string? name, Gender gender) : this(name, int.MaxValue, gender)
{
}
}
class Student(string? name, int age, Gender gender)
{
public string Id => $"{age}1001A";
}
class Student(string? name, int age, Gender gender)
{
public int Age { get; } = age; // 自己定义出来的自动属性会生成一个字段
public string Id => $"{age}1001A"; // 方法体内捕获参数会隐式生成一个字段
}
// 可以的情况
readonly ref struct Span<T>(in T reference)
{
private readonly ref T _reference = ref reference;
}
// 不可以的情况
class Student(ref int ageReference)
{
public string Id => $"{ageReference}1001A"; // 错误
}