第 9 节 · 第一部分 基础语法

常量

字面量是直接写在代码里的固定值,写法决定类型;const 给编译时就确定的值起名字,readonly 给运行时才确定、之后不再改的字段。

时长 11:08授课计划第 3 周

学完这一节,你能

讲义

跟着视频的顺序写。每行代码都有注释,代码和运行结果都是实际运行得到的。

1常量是什么

常量是定义好以后不能再重新赋值的固定值,比如圆周率、最大连接数。C# 有两种办法:直接把值写进代码,叫字面量;或者用 const 给它起个名字。下面先讲整数、小数、字符、字符串四种字面量,最后讲 const。

2整数常量

同一个数能用三种进制写:十进制直接写,十六进制加 0x,二进制加 0b。写法不同,值相同,三行打印出来都是 42;内存里存的都是二进制,进制只是写法。

C# · 第 7-9 行
7int decimalNumber = 42;// 十进制:直接写数字
8int hexNumber = 0x2A;// 十六进制:0x 开头,2A = 2×16 + 10 = 42
9int binaryNumber = 0b101010;// 二进制:0b 开头,101010 = 32 + 8 + 2 = 42

学过 C 语言要注意:C 里 0 开头是八进制,C# 没有八进制写法。

C# · 第 7-8 行
7int n = 0213;// 0 开头在 C# 里还是十进制
8Console.WriteLine(n);// 输出 213,不是八进制的 139

合法与不合法

数字中间可以用下划线分组,如 1_000_000。0b102(二进制里有 2)、0xFG、032UU(U 写了两次)都报 CS1002,123_(下划线在最后)报 CS1013。

C# · 第 7 行
7var n = 0b102;// var:让编译器自己推断类型;二进制里不能有 2
编译报错
Program.cs(7,21): error CS1002: 应输入 ;

后缀决定类型

后缀大小写都行:不带后缀从 int 开始挑,装不下依次换 uint、long、ulong;带 U 先 uint 再 ulong;带 L 先 long 再 ulong;UL 或 LU 直接是 ulong。

C# · 第 8-11 行
8Console.WriteLine("3000000000 " + (3000000000).GetType().Name);// GetType().Name 取出真实类型:超出 int,换成 UInt32
9Console.WriteLine("10000000000 " + (10000000000).GetType().Name);// uint 也装不下,换成 long(Int64)
10Console.WriteLine("100U " + (100U).GetType().Name);// U:先用 uint
11Console.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.14,也可以用科学计数法:e(大小写都行)表示 10 的几次方,1.5e3 小数点右移三位是 1500,2.5E-2 左移两位是 0.025。三种类型靠后缀区分:

  • f:float,单精度,约 6~9 位有效数字。
  • 不写或 d:double,双精度,约 15~17 位。
  • m:decimal,十进制高精度,适合算钱。
C# · 第 10-12 行
10Console.WriteLine($"314159E-5 → {314159E-5} ({314159E-5.GetType().Name})");// 插值字符串(后面讲);左移 5 位得 3.14159
11Console.WriteLine($"210f → {210f} ({210f.GetType().Name})");// 没有小数点也能加 f:float(Single),值 210
12Console.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,.e55(点前点后都没数字)报 CS1525,3.14L(L 只跟整数)和 1.2.3(两个小数点)报 CS1002。

最容易错的一点:没有后缀的小数默认是 double,直接赋给 float、decimal 会报错。

C# · 第 7-8 行
7float a = 3.14;// 3.14 是 double,要写 3.14f
8decimal 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。
C# · 第 9-18 行
9char unicodeChar = '\u4E2D';// \u 加 4 位十六进制:4E2D 就是“中”
10
11Console.WriteLine("普通字符:" + letter);// letter 是 'A',输出 A
12Console.WriteLine("中文字符:" + chinese);// chinese 是 '中',输出 中
13Console.WriteLine("Unicode 字符:" + unicodeChar);// 也输出 中
14
15Console.WriteLine("Hello\tWorld");// \t 制表:中间空出一段,宽度由终端决定
16Console.WriteLine("第一行\n第二行");// \n 换行:分成两行
17Console.WriteLine("双引号:\"");// \" 输出一个双引号
18Console.WriteLine("反斜杠:\\");// \\ 输出一个反斜杠
查看完整输出(8 行)
运行结果
普通字符:A
中文字符:中
Unicode 字符:中
Hello	World
第一行
第二行
双引号:"
反斜杠:\

C 语言的八进制转义(如 \101)和 \?,C# 都不认(CS1009),问号直接写就行。很多 Emoji 占两个代码单元,塞不进 char('\U0001F600' 也一样),要用 string:

C# · 第 7 行
7char 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。

教程的例子,四个字符串打印出五行:

C# · 第 10-19 行
10string a = "Hello\nWorld";// 普通字符串:\n 把它拆成两行
11
12// 逐字字符串
13string b = @"C:\Users\Public";// @ 开头:反斜杠照原样
14
15// 插值字符串
16string c = $"Hello, {name}!";// $ 开头:{name} 换成“小明”
17
18// 字符串拼接
19string d = "Hello, " + "World!";// + 把两个字符串拼成一个
运行结果
Hello
World
C:\Users\Public
Hello, 小明!
Hello, World!

两个字符串挨着写,C# 不会自动拼,要用 + 或插值:

C# · 第 7 行
7string s = "Hello, " "World!";// 中间没有 +
编译报错
Program.cs(7,30): error CS1002: 应输入 ;

6用 const 定义常量

写法是 const 类型 名字 = 常量表达式;。视频把它比成仪表箱:卡槽里的卡片随时能换,是变量;铸在面板上的铭牌改不了,是常量。

C# · 第 5-8 行
5const int MaxCount = 100;// 整数常量
6const double Pi = 3.1415926;// 小数常量
7const string SiteName = "C# 教程";// 字符串常量
8const bool IsEnabled = true;// 布尔常量

常量必须在声明时就给值(不给报 CS0145),而且值要在编译时就能算出来。const 只能用数值类型、bool、char、string 和枚举(后面会讲),其他引用类型(后面会讲)只能是 null;运行时才知道的值(如 DateTime.Now)可以考虑 readonly:

C# · 第 5 行
5const 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。

C# · 第 11-12 行
11public const int c1 = 5;// c1 = 5
12public const int c2 = c1 + 5;// 编译时就算好:c2 = 10
C# · 第 27-28 行
27Console.WriteLine("c1 = {0}, c2 = {1}",// {0}{1} 依次换成后面的值(参数在下一行)
28 SampleClass.c1, SampleClass.c2);// 用类名点出常量:类里的 const 自动是静态的
运行结果
x = 11, y = 22
c1 = 5, c2 = 10

硬改常量,编译器直接拦下:

C# · 第 10 行
10MaxCount = 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 适合运行时初始化后不再改的字段。
C# · 第 9-15 行
9readonly DateTime createdAt;// readonly:声明时可以先不给值
10
11// 在构造函数中初始化 readonly 字段
12public Program()// 构造函数 Program:创建对象时执行
13{// 构造函数开始
14 createdAt = new DateTime(2026, 1, 1);// 在构造函数里赋值:2026 年 1 月 1 日
15}// 构造函数结束
运行结果
圆周率:3.14159
创建日期:2026-01-01

在构造函数以外的方法里再赋值就报错:

C# · 第 14 行
14createdAt = new DateTime(2027, 1, 1);// 在普通方法 Change 里改 readonly 字段
编译报错
Program.cs(14,9): error CS0191: 无法分配到只读字段(除非在定义了该字段的类型的构造函数或 init-only 资源库中,或者在变量初始值设定项中)

引用类型的 readonly 字段只是不能换成别的对象(换了报 CS0198),对象里的数据照样能改:

C# · 第 10-11 行
10scores.Add(90);// scores 是 static readonly 的 List<int>(装一串 int 的列表,后面会讲);加一个数:允许
11Console.WriteLine(scores.Count);// Count 是元素个数,输出 1

8小结

  • 整数有三种进制和后缀,C# 没有八进制;小数靠后缀分 float、double、decimal,没后缀是 double。
  • 字符用单引号和转义序列,一个 char 只放一个代码单元;字符串有普通、逐字、插值、原始四种写法。
  • 编译时就确定、永远不变的值用 const;运行时才确定、之后不再改的字段用 readonly。

下一节:有了变量和常量,怎么让它们算起来、比起来?

检查题

5 道,选完马上告诉你对不对。

  1. 1Console.WriteLine(0213); 输出?

  2. 2float a = 3.14; 会怎样?

  3. 3下面哪个能把路径 C:\Users 原样写进字符串?

  4. 4const int MaxCount = 100; 之后写 MaxCount = 200;?

  5. 5想让 createdAt 在对象创建时才定下来、之后不能改,用?