对于不可变类型(immutable types),我们可以通过只读自动属性来创建(read-only auto-properties),也就是对属性只设置 get
访问器:
public string FirstName { get; }
public string LastName { get; }
此时,FirstName
和 LastName
仅能在类的构造器方法中才能赋值:
public Student(string firstName, string lastName)
{
if (IsNullOrWhiteSpace(lastName))
throw new ArgumentException(message: "Cannot be blank", paramName: nameof(lastName));
FirstName = firstName;
LastName = lastName;
}
如果在其他方法中尝试对只读自动属性赋值,那么就会产生 CS0200
编译时错误:
public class Student
{
public string LastName { get; }
public void ChangeName(string newLastName)
{
// Generates CS0200: Property or indexer cannot be assigned to -- it is read only
LastName = newLastName;
}
}
介绍完了新增的只读自动属性,接下来该说一说与之容易混淆的 const
和 readonly
了。
const
const
修饰符作用于字段或变量之上,表示修饰的字段或变量包含的值是个常量,不能改变的。它的约束发生于编译时,而不是运行时,换句话说,也就是在应用 const
修饰符的时候就需要同时给它赋值。有个地方需要注意,声明了 const
的字段会自动变为 static
字段,提升到类级别之上。其实这也是很好理解的,因为在编译时就固定下来的值,运行时无法改变,没必后期为每个实例都开辟空间存放这个固定不变的值。但是也很奇怪,如果某个字段声明为了 const
,就无法继续使用 static
来修饰了,不然编译会发生 CS0504
错误,我个人觉得其实继续显式声明本无可厚非,大家使用的时候注意点,明白就好。
class ConvertUnits
{
public const float CentimetersPerInch = 2.54F;
public const int CupsPerGallon = 16;
// ...
}
还有一点需要注意,const
字段只能够应用于那些具有字面量值类型的字段上(比如:string
, int
, double
等),Program
或者 System.Guid
这样的类型是不行的。
提示:除了上面说到的const
字段会自动转变为static
字段这样的优化之外,针对它还有另一个优化:程序集的引用。如果一个程序集引用了另外一个程序集中的const
,那么被引用的程序集中的const
会直接编译到引用的程序集中,那么,如果后期被引用的程序集中的const
值发生了变化,而引用程序集没有继续对其进行编译,则这个引用程序集引用中的const
值不会发生,除非对这个程序集重新编译。所以,const
修饰的值最好是那些始终不会变化的值,比如:PI。
readonly
接下来该着重说一下 readonly
了,先列出它和 const
的几点区别:
readonly
修饰符仅能应用于字段上(不能应用于本地变量)readonly
修饰符不仅可以在字段声明的时候赋值,还可以在构造器方法中对其修饰的字符进行赋值(赋值发生于运行时而非编译时)- 鉴于第 2 条区别,
readonly
修饰的字段不仅可以作为实例级别的,也可以作为类级别的(显式使用static
修饰) readonly
修饰的字段类型不限于那些具有字面量值的类型,而是所有数据类型
因为 readonly
可以将字段设为只读的,那么除字段声明和构造器方法之外,对其赋值,都会产生错误。所以,在代码中最好的方式就是不要直接对这样的字段做直接的读取,而是通过它的 get
属性,这样可以防止对它的意外赋值行为。这样的约束也是不难理解的,那么 C# 6.0 中新增的 只读自动属性 是不是顺其自然的事,其实它就是这种约束的语法糖。
参考资料:
- [What's New in C# 6 | Microsoft Docs](https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-6)
- Essential C# 6.0, 5th Edition