Skip to content

C# - 常量

在 C# 中,字面量是直接出现在代码中的固定值(例如 101、3.14、"Hello")。常量是定义后其值不能改变的变量。通过为固定值赋予有意义的名称,常量对于创建可读、可维护且无错误的代码至关重要。

C# 提供了两个关键字来声明常量:const 和 readonly。理解它们之间的区别至关重要。

整数字面量可以是十进制(基数 10)、十六进制(基数 16,带 0x 前缀)或二进制(基数 2,带 0b 前缀)。你可以使用 U(无符号)、L(长整型)和 UL(无符号长整型)等后缀来指定类型。

int decimalLiteral = 42;
long longLiteral = 42L;
// 十六进制 (十进制 255)
int hexLiteral = 0xFF;
// 二进制 (十进制 10) - C# 7.0+
int binaryLiteral = 0b1010;
// 数字分隔符,提高可读性 - C# 7.0+
long bigNumber = 1_000_000_000L;

这些表示实数。float 类型使用 f 后缀,double 类型使用 d(可选,因为它是默认值),decimal 类型使用 m(用于高精度金融计算)。

float piFloat = 3.14159f;
double piDouble = 3.1415926535;
decimal price = 29.99m;

字符字面量用单引号('a')括起来。字符串字面量用双引号("Hello")括起来。C# 提供了几种强大的定义字符串的方式:

// 带有换行符转义序列的标准字符串
string standard = "Hello,\nWorld!";
// 逐字字符串:忽略转义序列,适用于文件路径
string path = @"C:\Users\Guest\Documents";
// 字符串插值 (C# 6.0+):直接嵌入表达式
var name = "Alice";
string greeting = $"Hello, {name}!"; // 输出:Hello, Alice!
// 原始字符串字面量 (C# 11+):适用于多行字符串或包含引号的文本
string json = """
{
"name": "Bob",
"age": 30
}
""";

这是初学者经常混淆的一个点。以下是它们之间的明确区别:

特性constreadonly
初始化必须在声明时初始化。可以在声明时或在构造函数中初始化。
赋值时间值在编译时设定。值在运行时设定。
类型支持仅限于内置类型(数值、布尔、字符串、枚举)和 null。可用于任何数据类型,包括自定义类。
上下文隐式 static。属于类,而非实例。可以是 static(类有一个值)或实例级别(每个对象有一个值)。

对那些真正、通用的,永不改变的常量(如数学值)使用 const。

using System;
public class CircleCalculator
{
// 编译时常量,通过类名访问
public const double Pi = 3.14159;
public static void Main()
{
Console.Write("Enter Radius: ");
// 使用 TryParse 进行安全的输入处理
if (double.TryParse(Console.ReadLine(), out double radius) && radius > 0)
{
double area = CircleCalculator.Pi * radius * radius;
Console.WriteLine($"A circle with radius {radius} has an area of {area:F2}.");
}
else
{
Console.WriteLine("Invalid input. Please enter a positive number.");
}
}
}

对那些在对象生命周期内固定不变,但可能因每个对象实例而异的值使用 readonly。

using System;
public class User
{
// 此值由构造函数设置,之后不能更改。
public readonly Guid UserId;
public readonly DateTime CreationTime;
public string Username { get; set; }
public User(string username)
{
// readonly 字段可以在这里,即构造函数中赋值。
UserId = Guid.NewGuid();
CreationTime = DateTime.UtcNow;
Username = username;
}
public void DisplayInfo()
{
Console.WriteLine($"User '{Username}' created on {CreationTime:yyyy-MM-dd} with ID: {UserId}");
}
}
public class Program
{
public static void Main()
{
var user1 = new User("jdoe");
user1.DisplayInfo();
// 以下行会导致编译错误:
// user1.CreationTime = DateTime.Now; // 错误: readonly 字段不能被赋值(构造函数除外)
}
}
  • 避免“魔法数字/字符串”:将代码中未命名的字面量替换为命名常量。if (userRole == 5) 不够清晰,但 if (userRole == UserRoles.Administrator) 则具有自解释性。
  • 配置:使用静态 readonly 字段来保存从文件加载的应用程序配置值,这些值在启动时加载。
  • const 用于性能优化:由于 const 值直接嵌入到编译后的代码(IL)中,访问它们可能略快,但这是一种微优化,通常不具显著意义。
  • readonly 用于灵活性:readonly 更灵活,通常是更好的选择,除非你有一个真正的编译时常量。