C# 常量
`const`、`readonly`、`static readonly`、以及它们的编译期内联行为。
Key Points`const` 在编译期就替换成字面量;`readonly` 在运行时确定,但之后不能改。
const
C#
const double Pi = 3.14159;
const string AppName = "MyApp";
const int MaxRetry = 3;
// const 可以是编译期表达式
const int Timeout = 60 * 1000;
Console.WriteLine($"{AppName} {Pi} {MaxRetry} {Timeout}");Output
MyApp 3.14159 3 60000const values must be computable at compile time: numbers, strings, booleans, null references. No new, no method calls, no config reads.
readonly
C#
class Config
{
public readonly DateTime CreatedAt;
public readonly List<string> Tags = new();
public Config()
{
CreatedAt = DateTime.Now; // 只能在构造函数里赋值
Tags.Add("默认"); // 集合内容仍可改
}
public void TryChange()
{
// CreatedAt = DateTime.Now; // 编译错误:只读
Tags.Add("新增"); // 可以,改的是内容不是引用
}
}
var c = new Config();
c.TryChange();
Console.WriteLine(c.Tags.Count);Output
2readonly locks the reference, not the contents. Tags cannot point elsewhere but Add is fine. For truly immutable contents use IReadOnlyList<T> or immutable collections.
static readonly
C#
class App
{
public static readonly string Version;
public static readonly int[] AllowedPorts = { 80, 443, 8080 };
static App()
{
Version = "1.0." + DateTime.Now.Year;
}
}
Console.WriteLine(App.Version);
Console.WriteLine(App.AllowedPorts.Length);Output
1.0.2026
3三者对比
| Modifier | Set when | Where assignable | Inlined |
|---|---|---|---|
const | 编译期 | 声明处 | 是 |
readonly | 运行期 | 声明处 / 构造函数 | 否 |
static readonly | 运行期 | 声明处 / 静态构造函数 | 否 |
**Avoid
const across assemblies.** Its value is inlined into consumers; change the source assembly without rebuilding them and they keep the old value — a nasty bug. Use static readonly for public constants.字面量后缀
C#
var i = 42; // int
var l = 42L; // long
var f = 3.14f; // float
var d = 3.14; // double
var m = 3.14m; // decimal
var u = 42u; // uint
var hex = 0xFF; // 255
var bin = 0b1010; // 10
var sep = 1_000_000; // 1000000,下划线只为可读
Console.WriteLine($"{i} {l} {f} {d} {m} {u} {hex} {bin} {sep}");Output
42 42 3.14 3.14 3.14 42 255 10 10000003.14 is a double by default; float needs f and decimal needs m. A missing suffix is one of the most common C# compile errors.
const
C#
const double Pi = 3.14159;
const string AppName = "MyApp";
const int MaxRetry = 3;
// const 可以是编译期表达式
const int Timeout = 60 * 1000;
Console.WriteLine($"{AppName} {Pi} {MaxRetry} {Timeout}");Output
MyApp 3.14159 3 60000const 只能是编译期能算出的值:数字、字符串、布尔、null 引用。不能用 new、不能调用方法、不能读配置文件。
readonly
C#
class Config
{
public readonly DateTime CreatedAt;
public readonly List<string> Tags = new();
public Config()
{
CreatedAt = DateTime.Now; // 只能在构造函数里赋值
Tags.Add("默认"); // 集合内容仍可改
}
public void TryChange()
{
// CreatedAt = DateTime.Now; // 编译错误:只读
Tags.Add("新增"); // 可以,改的是内容不是引用
}
}
var c = new Config();
c.TryChange();
Console.WriteLine(c.Tags.Count);Output
2readonly 锁的是引用,不是对象内容。Tags 不能指向别的 List,但 Add 完全合法。想连内容都不能改,用 IReadOnlyList<T> 或不可变集合。
static readonly
C#
class App
{
public static readonly string Version;
public static readonly int[] AllowedPorts = { 80, 443, 8080 };
static App()
{
Version = "1.0." + DateTime.Now.Year;
}
}
Console.WriteLine(App.Version);
Console.WriteLine(App.AllowedPorts.Length);Output
1.0.2026
3三者对比
| 修饰符 | 何时确定 | 可在哪赋值 | 是否内联 |
|---|---|---|---|
const | 编译期 | 声明处 | 是 |
readonly | 运行期 | 声明处 / 构造函数 | 否 |
static readonly | 运行期 | 声明处 / 静态构造函数 | 否 |
**跨程序集不要用
const**。它的值被内联到使用方,改了源程序集但使用方没重新编译,就还是旧值——这种 bug 极难排查。公共常量一律用 static readonly。字面量后缀
C#
var i = 42; // int
var l = 42L; // long
var f = 3.14f; // float
var d = 3.14; // double
var m = 3.14m; // decimal
var u = 42u; // uint
var hex = 0xFF; // 255
var bin = 0b1010; // 10
var sep = 1_000_000; // 1000000,下划线只为可读
Console.WriteLine($"{i} {l} {f} {d} {m} {u} {hex} {bin} {sep}");Output
42 42 3.14 3.14 3.14 42 255 10 10000003.14 默认是 double,赋给 float 必须加 f,赋给 decimal 必须加 m。漏了后缀是 C# 里最常见的编译错误之一。