const 和readonly 相似,但并不完全相同。
const 字段是编译时常量,这意味着该值可以在编译时计算。 readonly 字段支持在构造类型期间必须运行某些代码的其他场景。构造完成后,readonly 字段无法更改。
例如,const 成员可用于定义如下成员:
struct Test
{
public const double Pi = 3.14;
public const int Zero = 0;
}
因为像 3.14 和 0 这样的值是编译时常量。但是,请考虑您定义类型并希望提供一些预制实例的情况。例如,您可能想要定义一个 Color 类并为黑色、白色等常见颜色提供“常量”。使用 const 成员是不可能的,因为右侧不是编译时常量。可以使用常规静态成员来做到这一点:
public class Color
{
public static Color Black = new Color(0, 0, 0);
public static Color White = new Color(255, 255, 255);
public static Color Red = new Color(255, 0, 0);
public static Color Green = new Color(0, 255, 0);
public static Color Blue = new Color(0, 0, 255);
private byte red, green, blue;
public Color(byte r, byte g, byte b) => (red, green, blue) = (r, g, b);
}
但是,没有什么可以阻止 Color 的客户使用它,也许是通过交换 Black 和 White 值。不用说,这会引起 Color 类的其他客户的恐慌。 “只读”功能解决了这种情况。
通过在声明中简单地引入readonly 关键字,我们保留了灵活的初始化,同时防止客户端代码乱七八糟。
public class Color
{
public static readonly Color Black = new Color(0, 0, 0);
public static readonly Color White = new Color(255, 255, 255);
public static readonly Color Red = new Color(255, 0, 0);
public static readonly Color Green = new Color(0, 255, 0);
public static readonly Color Blue = new Color(0, 0, 255);
private byte red, green, blue;
public Color(byte r, byte g, byte b) => (red, green, blue) = (r, g, b);
}
有趣的是,const 成员始终是静态的,而 readonly 成员可以是静态的也可以不是静态的,就像常规字段一样。
可以为这两个目的使用一个关键字,但这会导致版本控制问题或性能问题。假设我们为此使用了一个关键字(const)并且开发人员写道:
public class A
{
public static const C = 0;
}
另一位开发人员编写了依赖于 A 的代码:
public class B
{
static void Main() => Console.WriteLine(A.C);
}
现在,生成的代码是否可以依赖 A.C 是编译时常量这一事实?即,是否可以简单地将 A.C 的使用替换为值 0?如果您对此说“是”,那么这意味着 A 的开发人员无法更改 A.C 的初始化方式——这在未经许可的情况下束缚了 A 的开发人员的手。
如果您对这个问题说“不”,那么就会错过重要的优化。也许 A 的作者肯定 A.C 将永远为零。 const 和 readonly 的使用允许 A 的开发人员指定意图。这有助于更好的版本控制行为和更好的性能。