1常量是什么
常量是定义好以后不能再重新赋值的固定值,比如圆周率、最大连接数。C# 有两种办法:直接把值写进代码,叫字面量;或者用 const 给它起个名字。下面先讲整数、小数、字符、字符串四种字面量,最后讲 const。
2整数常量
同一个数能用三种进制写:十进制直接写,十六进制加 0x,二进制加 0b。写法不同,值相同,三行打印出来都是 42;内存里存的都是二进制,进制只是写法。
| 7 | int decimalNumber = 42; | // 十进制:直接写数字 |
| 8 | int hexNumber = 0x2A; | // 十六进制:0x 开头,2A = 2×16 + 10 = 42 |
| 9 | int binaryNumber = 0b101010; | // 二进制:0b 开头,101010 = 32 + 8 + 2 = 42 |
学过 C 语言要注意:C 里 0 开头是八进制,C# 没有八进制写法。
| 7 | int n = 0213; | // 0 开头在 C# 里还是十进制 |
| 8 | Console.WriteLine(n); | // 输出 213,不是八进制的 139 |
合法与不合法
数字中间可以用下划线分组,如 1_000_000。0b102(二进制里有 2)、0xFG、032UU(U 写了两次)都报 CS1002,123_(下划线在最后)报 CS1013。
| 7 | var n = 0b102; | // var:让编译器自己推断类型;二进制里不能有 2 |
Program.cs(7,21): error CS1002: 应输入 ;
后缀决定类型
后缀大小写都行:不带后缀从 int 开始挑,装不下依次换 uint、long、ulong;带 U 先 uint 再 ulong;带 L 先 long 再 ulong;UL 或 LU 直接是 ulong。
| 8 | Console.WriteLine("3000000000 " + (3000000000).GetType().Name); | // GetType().Name 取出真实类型:超出 int,换成 UInt32 |
| 9 | Console.WriteLine("10000000000 " + (10000000000).GetType().Name); | // uint 也装不下,换成 long(Int64) |
| 10 | Console.WriteLine("100U " + (100U).GetType().Name); | // U:先用 uint |
| 11 | Console.WriteLine("5000000000U " + (5000000000U).GetType().Name); | // uint 放不下,用 ulong(UInt64) |
查看完整输出(8 行)
100 Int32 3000000000 UInt32 10000000000 Int64 100U UInt32 5000000000U UInt64 100L Int64 100UL UInt64 100lu UInt64
后缀 L 最好写大写:long n = 100l; 能编译,但会警告 CS0078: “l”后缀容易与数字“1”混淆;为清楚起见,请使用“L”。
3浮点常量
小数可以写成 3.,也可以用科学计数法:e(大小写都行)表示 10 的几次方,1. 小数点右移三位是 1500,2. 左移两位是 0.025。三种类型靠后缀区分:
f:float,单精度,约 6~9 位有效数字。- 不写或
d:double,双精度,约 15~17 位。 m:decimal,十进制高精度,适合算钱。
| 10 | Console.WriteLine($"314159E-5 → {314159E-5} ({314159E-5.GetType().Name})"); | // 插值字符串(后面讲);左移 5 位得 3.14159 |
| 11 | Console.WriteLine($"210f → {210f} ({210f.GetType().Name})"); | // 没有小数点也能加 f:float(Single),值 210 |
| 12 | Console.WriteLine($"1.5e3 → {1.5e3} ({1.5e3.GetType().Name})"); | // 1.5 × 10³ = 1500,Double |
查看完整输出(8 行)
3.14159 → 3.14159 (Double) 3.14f → 3.14 (Single) 3.14m → 3.14 (Decimal) 314159E-5 → 3.14159 (Double) 210f → 210 (Single) 1.5e3 → 1500 (Double) 0.25D → 0.25 (Double) 2.5E-2 → 0.025
不合法的:510E(E 后没数字)报 CS0595,.(点前点后都没数字)报 CS1525,3.(L 只跟整数)和 1.(两个小数点)报 CS1002。
最容易错的一点:没有后缀的小数默认是 double,直接赋给 float、decimal 会报错。
| 7 | float a = 3.14; | // 3.14 是 double,要写 3.14f |
| 8 | decimal c = 99.99; | // 99.99 是 double,要写 99.99m |
Program.cs(7,19): error CS0664: 无法将 Double 类型隐式转换为“float”类型;请使用“F”后缀创建此类型 Program.cs(8,21): error CS0664: 无法将 Double 类型隐式转换为“decimal”类型;请使用“M”后缀创建此类型
decimal 适合算钱,但任何数值类型都有范围和精度的限制。
4字符常量
字符常量用单引号,如 'A'、'中',类型是 char,表示一个 UTF-16 代码单元(C# 用 UTF-16 编码存字符,一个代码单元占 2 字节)。打不出来的字符用反斜杠加字母写,叫转义序列;\n 看着两个字符,其实只是一个换行符。
\\反斜杠,\'单引号,\"双引号;\0空字符,\a响铃,\b退格,\f换页,\n换行,\r回车,\t水平制表,\v垂直制表。\x跟 1~4 位十六进制、\u跟 4 位,都能写出一个字符;\U跟 8 位,能写出任何 Unicode 码点(字符在 Unicode 里的编号),但放进char最多到\U0000FFFF。
| 9 | char unicodeChar = '\u4E2D'; | // \u 加 4 位十六进制:4E2D 就是“中” |
| 10 | | |
| 11 | Console.WriteLine("普通字符:" + letter); | // letter 是 'A',输出 A |
| 12 | Console.WriteLine("中文字符:" + chinese); | // chinese 是 '中',输出 中 |
| 13 | Console.WriteLine("Unicode 字符:" + unicodeChar); | // 也输出 中 |
| 14 | | |
| 15 | Console.WriteLine("Hello\tWorld"); | // \t 制表:中间空出一段,宽度由终端决定 |
| 16 | Console.WriteLine("第一行\n第二行"); | // \n 换行:分成两行 |
| 17 | Console.WriteLine("双引号:\""); | // \" 输出一个双引号 |
| 18 | Console.WriteLine("反斜杠:\\"); | // \\ 输出一个反斜杠 |
查看完整输出(8 行)
普通字符:A 中文字符:中 Unicode 字符:中 Hello World 第一行 第二行 双引号:" 反斜杠:\
C 语言的八进制转义(如 \101)和 \?,C# 都不认(CS1009),问号直接写就行。很多 Emoji 占两个代码单元,塞不进 char('\U0001F600' 也一样),要用 string:
| 7 | char face = '😀'; | // 这个 Emoji 占两个代码单元 |
Program.cs(7,21): error CS1012: 字符字面量中的字符太多
5字符串常量
字符串用双引号,类型是 string,像一整排字符。四种写法:
- 普通:可以转义,路径的反斜杠要写两遍,如
"C:\\Users\\Public\\Documents"。 - 逐字:加
@,反斜杠照原样,如@"C:\Users\Public\Documents";双引号连写两个""。 - 插值:加
$,花括号里放变量或表达式,如$"名称:{name},年龄:{age}"。 - 原始(C# 11 起):三个双引号
"""包起来,反斜杠、双引号都不用转义,适合 JSON、HTML、SQL。
教程的例子,四个字符串打印出五行:
| 10 | string a = "Hello\nWorld"; | // 普通字符串:\n 把它拆成两行 |
| 11 | | |
| 12 | // 逐字字符串 | |
| 13 | string b = @"C:\Users\Public"; | // @ 开头:反斜杠照原样 |
| 14 | | |
| 15 | // 插值字符串 | |
| 16 | string c = $"Hello, {name}!"; | // $ 开头:{name} 换成“小明” |
| 17 | | |
| 18 | // 字符串拼接 | |
| 19 | string d = "Hello, " + "World!"; | // + 把两个字符串拼成一个 |
Hello World C:\Users\Public Hello, 小明! Hello, World!
两个字符串挨着写,C# 不会自动拼,要用 + 或插值:
| 7 | string s = "Hello, " "World!"; | // 中间没有 + |
Program.cs(7,30): error CS1002: 应输入 ;
6用 const 定义常量
写法是 const 类型 名字 = 常量表达式;。视频把它比成仪表箱:卡槽里的卡片随时能换,是变量;铸在面板上的铭牌改不了,是常量。
| 5 | const int MaxCount = 100; | // 整数常量 |
| 6 | const double Pi = 3.1415926; | // 小数常量 |
| 7 | const string SiteName = "C# 教程"; | // 字符串常量 |
| 8 | const bool IsEnabled = true; | // 布尔常量 |
常量必须在声明时就给值(不给报 CS0145),而且值要在编译时就能算出来。const 只能用数值类型、bool、char、string 和枚举(后面会讲),其他引用类型(后面会讲)只能是 null;运行时才知道的值(如 DateTime.)可以考虑 readonly:
| 5 | const DateTime Today = DateTime.Now; | // DateTime 不能做 const,Now 也不是常量 |
Program.cs(5,5): error CS0283: 不能将类型“DateTime”声明为 const Program.cs(5,28): error CS0133: 指派给“Program.Today”的表达式必须是常量
教程的例子:类里有普通字段 x、y 和常量 c1、c2,对象创建时传进 11 和 22。
| 11 | public const int c1 = 5; | // c1 = 5 |
| 12 | public const int c2 = c1 + 5; | // 编译时就算好:c2 = 10 |
| 27 | Console.WriteLine("c1 = {0}, c2 = {1}", | // {0}{1} 依次换成后面的值(参数在下一行) |
| 28 | SampleClass.c1, SampleClass.c2); | // 用类名点出常量:类里的 const 自动是静态的 |
x = 11, y = 22 c1 = 5, c2 = 10
硬改常量,编译器直接拦下:
| 10 | MaxCount = 200; | // MaxCount 是 const,不能重新赋值 |
Program.cs(10,9): error CS0131: 赋值号左边必须是变量、属性或索引器
7const 和 readonly
readonly 像出厂时打的钢印:组装时打一次,之后改不了。下面的例子里 Pi 是 const(3.14159),createdAt 是 readonly:
const编译时就定了,必须声明时赋值;readonly可以等到构造函数(创建对象时自动执行的方法)里再赋值。赋好以后都不能在普通代码里重新赋值。const只支持那几种类型,readonly支持的更多。- 类里的
const自动是静态的;readonly可以是实例字段(每个对象各有一份),也可以加static。 const适合数学常量、状态标识;readonly适合运行时初始化后不再改的字段。
| 9 | readonly DateTime createdAt; | // readonly:声明时可以先不给值 |
| 10 | | |
| 11 | // 在构造函数中初始化 readonly 字段 | |
| 12 | public Program() | // 构造函数 Program:创建对象时执行 |
| 13 | { | // 构造函数开始 |
| 14 | createdAt = new DateTime(2026, 1, 1); | // 在构造函数里赋值:2026 年 1 月 1 日 |
| 15 | } | // 构造函数结束 |
圆周率:3.14159 创建日期:2026-01-01
在构造函数以外的方法里再赋值就报错:
| 14 | createdAt = new DateTime(2027, 1, 1); | // 在普通方法 Change 里改 readonly 字段 |
Program.cs(14,9): error CS0191: 无法分配到只读字段(除非在定义了该字段的类型的构造函数或 init-only 资源库中,或者在变量初始值设定项中)
引用类型的 readonly 字段只是不能换成别的对象(换了报 CS0198),对象里的数据照样能改:
| 10 | scores.Add(90); | // scores 是 static readonly 的 List<int>(装一串 int 的列表,后面会讲);加一个数:允许 |
| 11 | Console.WriteLine(scores.Count); | // Count 是元素个数,输出 1 |
8小结
- 整数有三种进制和后缀,C# 没有八进制;小数靠后缀分
float、double、decimal,没后缀是double。 - 字符用单引号和转义序列,一个
char只放一个代码单元;字符串有普通、逐字、插值、原始四种写法。 - 编译时就确定、永远不变的值用
const;运行时才确定、之后不再改的字段用readonly。
下一节:有了变量和常量,怎么让它们算起来、比起来?
