摘要:C# 注释怎么写?这篇讲清单行注释、块注释、XML 文档注释三种写法,每种都能直接复制运行。动手验证注释不能嵌套会报 CS1525、双引号里的注释符号不会被解析、文档注释贴错位置编译器也不拦你,再实测开启文档生成后触发 CS1591 警告的全过程。哪些注释该写、哪些写了等于没写,批量注释快捷键怎么用,一次说透。零基础入门 · 第 4 篇。
难度:★☆☆☆☆ 预计阅读 10 分钟
上一篇我们把 Program.cs 里那 12 行拆了个干净。收尾时你大概已经发现一个问题:每一行代码都清清楚楚地说了"做了什么",但没有一行说"为什么这么做"。
三个月后你回来改这段代码,Console.WriteLine 还能看懂,可当初为什么在这儿多判一个条件、那个魔数 3600 是哪来的,翻遍整个文件也找不到答案。注释就是为这件事准备的。
这篇目标很具体:你能判断一段代码该不该加注释、该加哪种、以及怎么让 Visual Studio 帮你写。
一、三种注释,先认个脸
C# 的注释一共三种,长得完全不一样:
写法 名字 范围 编译器看它吗
// 单行注释 到这一行结尾为止 完全无视
/* */ 块注释 中间夹住的所有内容 完全无视
/// XML 文档注释 三斜杠,贴成员 会读,但只用来生成文档
前两种是纯粹给人看的,写多写少都不影响程序跑。第三种不一样——编译器会去读它,用它生成一份 XML 文档文件,某些设置下还会因为它报警告。这条后面单独讲。
现在打开你的 Program.cs,把里面全部内容删掉,原样敲下面这段:
using System; namespace HelloWorld; class Program { static void Main(string[] args) { // 这是一行单行注释,打印一句话 Console.WriteLine("第一行"); /* 这是一段块注释 可以横跨好几行 里面想写几行写几行 */ Console.WriteLine("第二行"); } }F5,输出:
第一行 第二行三条注释一个都没出现在输出里,程序也跑得好好的。这就是注释的全部含义:写给人看的东西,编译器直接跳过。
顺便验证一下编译器有多"无视":把上面那个 Console.WriteLine(“第一行”); 整行前面加上 //,再 F5。它不会执行了,输出只剩一行 第二行。
再次 F5 之前记得把那两斜杠删掉。 留着注释掉一行代码到处跑,是新手最容易攒下的一堆坑。
二、// —— 前面带一行,后面带一句
// 的规则只有一条:从这两个斜杠开始,到这一行结尾为止,全是注释。
它有两种摆法。
摆法一:写在代码上一行。 这是主力用法,用来解释接下来这段代码在干什么。
// 把用户输入的名字原样打印出来,不做任何加工 Console.WriteLine(name);摆法二:写在代码后面。 只在补一句很短的话时用。
Console.WriteLine("第一行"); // 用换行结尾,下一句才能对齐 Console.WriteLine("第二行");摆法二别连着用。 连续三行都挂尾注,代码会横向拖得很长,很难读:
int a = 1; // 初始化 a int b = 2; // 初始化 b int c = 3; // 初始化 c 这个注释纯属废话因为谁都能看出来上面第三行那个注释就是典型的废话注释,第三篇说过:代码已经说清楚的事,注释就别再说一遍。判断标准很简单——删掉这行注释,读代码的人会不会卡住?不会,就删掉。
为什么 // 不能跨行
因为它的范围被"行尾"卡死了。C# 编译器扫代码时遇到换行符就知道这条注释结束了。
有个例外要记住:字符串里不算行尾。 下一节专门讲这个。
三、/* */ —— 一次盖好几行
/* 开始,*/ 结束,中间夹多少行都算注释。
它存在的理由只有一个:// 一次只能注释一行,而块注释可以一口气盖掉一整段。
/* 下面这三种算法各有各的适用范围: 1. 冒泡排序:实现简单,适合几十条的小数据量 2. 快速排序:平均最快,需要递归 3. 归并排序:最稳定,但要额外开一块内存 暂时都不需要,先留着备查 */// 也能写这么长,只要每行前面都补一个 //:
// 下面这三种算法各有各的适用范围: // 1. 冒泡排序:实现简单,适合几十条的小数据量 // 2. 快速排序:平均最快,需要递归 // 3. 归并排序:最稳定,但要额外开一块内存 // 暂时都不需要,先留着备查这两种写法效果完全一样。 那为什么还要 /* */?有个实际原因——给代码临时注释掉的时候,// 更安全,下一节就讲为什么。
一个纯排版习惯
块注释每行前面习惯性加一个 *:
/* * 这是一个块注释 * 这是第二行 */开头那个 /* 后面不加 *,这是行规不是语法。编译器只认 */ 两个字符收尾,加不加 * 它都不管。这样写纯粹为了对齐好看、扫一眼就知道是注释的开始。你要是不加,也不算错。
四、关键实验:块注释不能嵌套
这条是本文最容易被网上文章讲错的地方,所以你自己跑一遍。
你可能听过这么一句话:“块注释里可以再写块注释,就像 C++ 那样。” 在 C# 里这是错的。
官方语言规范里写得很明确:“Comments do not nest.”(注释不嵌套。)
现在动手试。打开 Program.cs,全部删掉,敲这段——就是故意写错:
using System; namespace HelloWorld; class Program { static void Main(string[] args) { /* 外层块注释开始 /* 内层块注释试图嵌套 */ Console.WriteLine("这行在嵌套注释里吗?"); */ Console.WriteLine("A"); } }F5,红字来了:
Program.cs(12,10): error CS1525: 表达式项“/”无效编译器到底干了什么
它根本没去区分"内层"和"外层"。它的做法简单到近乎粗暴:遇到第一个 */ 就认定注释结束。
所以这段代码实际是这样解析的:
行 编译器怎么理解
/* 外层块注释开始 注释开始
/* 内层块注释试图嵌套 */ 还在注释里。这行的 */ 把注释关掉了
Console.WriteLine(“这行在嵌套注释里吗?”); 注释已经关了,这是真正的代码
*/ 代码里冒出个没头没尾的 */,报错
注意第 3 行——你以为它在注释里,其实它是一句货真价实的可执行语句,只不过那句 Console.WriteLine 真的被执行了。
报错位置报在第 12 行那个孤零零的 */,也就是注释提前关闭之后编译器第一个撞上"不该出现的字符"的地方。真正的问题在第 10 行。
那要怎么注释一大段代码
用 // 一行一行加,别用 /* */ 包起来。也可以用 VS 的快捷键——第四节末尾会说。
记住这条规则的用处在于:以后看到 CS1525 且报错行里有个 */,不用怀疑自己,直接去找上面有没有一个块注释被提前关掉了。
五、双引号里的 // 不是注释
这条听起来像废话,但它解释了为什么"注释掉一大段代码"用 // 比用 /* */ 安全。
编译器在字符串里的行为:不解析注释。 双引号包起来的内容,一个字符都不当语法看。
打开 Program.cs,全部替换成这段:
using System; namespace HelloWorld; class Program { static void Main(string[] args) { // 双引号里的 // 不是注释 Console.WriteLine("这里有个 // 符号"); // 双引号里的 /* 也不是注释 Console.WriteLine("这里有个 /* 符号"); // 双引号里的 */ 同样不是注释 Console.WriteLine("这里有个 */ 符号"); } }F5,输出:
这里有个 // 符号 这里有个 /* 符号 这里有个 */ 符号三个符号全打印出来了,一个都没让编译器出错。
这解释了一件事
假设你有一段代码,里面正好有一行字符串带 */:
string endMark = "*/";你嫌它暂时用不上,想整段注释掉,于是手一抖:
/* 暂时不用这段 string endMark = "*/"; Console.WriteLine(endMark); */结果就是你上一节看到的 CS1525——注释在字符串里那个/ 处提前关闭了。问题不在 // 和 /上,在 */ 上。 而用 // 逐行注释,根本不会碰到这个坑。
六、/// —— 唯一一种编译器会读的注释
前两种注释编译器完全无视。第三种不一样。
术语解释:XML 文档注释(XML documentation comment)指用三个斜杠 /// 开头、写在类、方法等成员正上方的一段说明。它的内容按 XML 格式书写,编译器会去读它,然后生成一份 .xml 文档文件。
写法长这样:
using System; namespace HelloWorld; /// <summary> /// 一个用来演示的程序。 /// </summary> class Program { /// <summary> /// 程序从这里开始执行。 /// </summary> static void Main(string[] args) { Console.WriteLine("A"); } }是标签,< 和 > 是标签的括号——这套写法来自 XML,一种用尖括号标记结构的纯文本格式。整个 C# 的注释体系里,这是唯一一处 // 和 /* 之外的语法味道。必须紧贴成员,中间不能有空行。 上面 /// … 和 class Program 之间只有换行、没有空行,这个 summary 才会算作 Program 的说明。中间插入一个空行,它就成了"悬空的注释",编译器读不到归属。
标签没写对会怎样
少写一个 <、 忘了闭合,都属于 XML 结构错误,会报:warning CS1570: XML 注释格式不正确让 Visual Studio 帮你写
不用背。光标放在要写说明的成员上,按 Ctrl+Shift+C(菜单路径:编辑 → 高级 → 添加 XML 文档注释),VS 自动插好骨架并把光标放进 和 中间,直接打字就行。
你也可以手动敲三个斜杠,VS 同样会自动补全标签。
一个重要实验:为什么你的控制台项目没报警告
很多教程说"写了 /// 却收到警告 CS1591",但你现在的项目加了两行 ///,构建面板干干净净:
0 个警告 0 个错误这不是你写对了,是你的项目压根没让编译器去管这件事。
想亲眼看到 CS1591,照着做:
- 在解决方案资源管理器里右键项目名 → 属性
- 顶部的下拉框从 活动配置 切到 所有配置
- 找到 生成 → 输出 那一栏
- 勾上 生成包含 API 注释的文件(英文项目里是 Generate XML documentation file)
等价的做法是直接在 .csproj 里加一行:
<PropertyGroup> <GenerateDocumentationFile>true</GenerateDocumentationFile> </PropertyGroup>注意:类名默认不写修饰符就是 internal(程序集内可见),不叫"公开可见"。 官方文档明确说 CS1591 只针对公开可见的类型和成员。所以光开这个勾选项还不会报。
再把 Program 前面补一个 public:
using System; namespace HelloWorld; public class Program { public static void Main(string[] args) { Console.WriteLine("A"); } }现在 F5(或者 Ctrl+Shift+B 只编译),警告列表里出现:
warning CS1591: 缺少对公共可见类型或成员“Program”的 XML 注释 warning CS1591: 缺少对公共可见类型或成员“Program.Main(string[])”的 XML 注释最后 bin\Debug\net10.0\ 目录下会多出一个 t1.xml(跟你项目名同名的那个)。打开它,里面就是编译器把你的 /// 转成的结构化文档:
<?xml version="1.0"?> <doc> <assembly> <name>t1</name> </assembly> <members> </members> </doc>members 是空的,因为你把 /// 删掉了。这就是 /// 的真正去处——它不是写给编辑器看的,是写给文档生成工具看的。
那要不要开这个设置
控制台项目、练手项目:不用开,也没必要开。 你是在学语法,不是在发布类库。
写给别人用的类库、需要生成 API 文档的项目:开,而且应该把每个公开成员都补上。 这是 .NET 生态里的行业惯例,官方文档管这叫"XML 注释"。
CS1591 只是警告,不是错误——程序照样能跑,不会拦住你。 这一点新手常搞混。
补一个反直觉的实验
有篇流传很广的说法是"文档注释贴错位置会报 CS1587"。在现在的编译器上不成立。
把 /// 贴在一个语句正前方,而不是成员上:
using System; namespace HelloWorld; class Program { static void Main(string[] args) { /// <summary> /// 这个文档注释贴错了位置,它紧贴的是一个语句。 /// </summary> Console.WriteLine("A"); } }编译结果:0 个警告,0 个错误。
编译器只是默默忽略了它。所以你把注释贴歪了,程序不会拦你——它也不会生效,那段说明不会出现在任何文档里。 靠编译器提醒是等不到的,只能自己检查。
七、VS 的注释快捷键
手敲 // 慢,尤其是给选中的一堆行批量加注释。四个快捷键解决:
快捷键 官方名称 干什么
Ctrl + / 切换行注释 选中的每一行前面加 //,已经有了就去掉
Ctrl + Shift + / 切换块注释 整段包上 /* */ 或拆掉
Ctrl + Shift + C 添加 XML 文档注释 生成 /// 骨架
Ctrl + K 再按 Ctrl + C 批注选择 老版本的注释方式,还能用
菜单路径都在 编辑 → 高级 下面。(上面第 4 节键位是 VS 2022 引入的,你用的是 VS 2026,直接支持。)
Ctrl + / 是最该记住的一个。 试一下:
- 选中 Main 方法里那四行
- 按 Ctrl + /
VS 给每一行前面都补上了 //,代码立刻变灰。再按一次,全部恢复原状。
它默认用 // 而不是 /* */,正是第四节那个实验的直接原因——逐行加注释不会遇到嵌套和提前闭合的坑。你手动用块注释包一大段代码之前,先想想里面有没有 */。
顺手提一个坑:这个快捷键在中文输入法下经常失灵,敲出来是个问号。原因是 Ctrl + / 和输入法切换快捷键撞了。解决:换成英文输入法再按;或者用 编辑 → 高级 → 切换行注释 点菜单,效果一样。
八、常见错误汇总
CS1525:表达式项“/”无效
你看到行尾有个孤零零的/,十有八九是被注释掉了。往上翻,找一个 /是不是在它之前就被别的 */ 提前关掉了。成因基本只有两个:块注释里嵌了块注释,或者被注释的代码里有一行字符串带 */。
注释贴歪了没任何提示
刚才那个实验证明了——/// 贴到语句前面,编译器一声不吭。这不是 bug,文档注释本来就只对成员生效。靠看代码检查,不靠等报错。
CS1591:缺少对公共可见类型或成员"xxx"的 XML 注释
只在你主动开了"生成包含 API 注释的文件"之后才可能出现,而且只针对 public / protected 的成员。三个办法:补上 ///、把成员改成 internal、或者干脆关掉那个勾选项。练手项目关掉最省事。
警告和错误不是一回事
CSxxxx 后面带 error 的是错误,编译直接失败,程序跑不起来。带 warning 的是警告,照常生成照常跑。新手看见"1 个警告"就慌,大可不必——只有错误才拦路。
打开 .csproj 时看到一堆看不懂的标签
第一节改设置需要动这个文件,文件里全是 、 之类的标签。别整段重写,只照着格式加一行 true,加错了会看到 CS5001 或者一堆 XML 解析报错。
练习题
三题都在 Program.cs 上动手。第 2、3 题建议先写下来再动手。
第 1 题
下面这段代码,F5 之后控制台上会打印几行?请写出每一行的内容,并说明原因:
using System; namespace HelloWorld; class Program { static void Main(string[] args) { Console.WriteLine("1"); /* 输出 1 */ /* Console.WriteLine("2"); */ Console.WriteLine("3"); // 输出 3 // Console.WriteLine("4"); /* Console.WriteLine("5"); Console.WriteLine("6"); */ } }第 2 题
下面这段代码你猜会报什么错?先写下来,再动手敲验证,看看和你猜的差多少:
using System; namespace HelloWorld; class Program { static void Main(string[] args) { /* 第一段注释 /* 第二段注释 */ int a = 1; */ Console.WriteLine(a); } }请回答:
(a)完整的报错编号和中文描述是什么?
(b)报错报在第几行?真正出问题的是第几行?
(c)int a = 1; 这一行,它会执行吗?为什么?
(d)把这四行注释写法改成一种能正常编译的写法。
第 3 题
下面是一段带注释的代码。请挑出至少三处不该写的注释,说明每处为什么多余,然后给出你改完之后的版本:
using System; namespace HelloWorld; class Program { static void Main(string[] args) { // 声明变量 int total = 0; // 循环 for (int i = 1; i <= 100; i++) { // 把 i 加到 total 上 total = total + i; } /* 输出结果 */ Console.WriteLine(total); } }参考答案
第 1 题
打印 两行,内容是 1 和 3。
逐行过一遍:
代码 执行吗 为什么
Console.WriteLine(“1”); 是 后面挂的 /* 输出 1/ 是块注释,只盖住自己那段
/Console.WriteLine(“2”); */ 否 整行在注释里
Console.WriteLine(“3”); 是 后面挂的 // 输出 3 同样只是备注
// Console.WriteLine(“4”); 否 整行在注释里
那两行 Console.WriteLine(“5”) / (“6”) 第五行否、第六行否 看起来像"注释掉了两行",但编译器不这么读
最后那两行是这道题的全部考点。编译器遇到第一行的 /* 开始注释,一路读到第二行的 */ 才结束。所以:
- Console.WriteLine(“5”); 在注释里 → 不执行
- Console.WriteLine(“6”); 在注释里 → 不执行
- 结尾那个/ 后面紧跟的是 }(类的闭合大括号)——注意这里没报错,纯属运气好。因为注释从 /到 */ 正好覆盖了两行,结尾的 */ 被消耗掉了,后面剩下的是完好的代码。
如果那两行再多写一句代码,末尾的/ 就会露出来,你会看到 CS1525。所以"这段看起来被注释了"不等于"这段真的被注释了",得数一下 /和 */ 的个数。
第 2 题
(a) 完整报错:
Program.cs(12,10): error CS1525: 表达式项“/”无效CS1525 的中文描述就是"表达式项“/”无效"。猜测不中很正常,编号本身不重要,重要的是它属于"多出来一个 / 符号"这一类,看到这个就该往注释方向找。
(b) 报错在第 12 行(那个孤零零的/),真正出问题的是第 10 行那个试图嵌套的内层 /。
编译器不做"嵌套"判断,它只认第一个 */。第 10 行末尾的 */ 把注释关掉了,第 11 行 int a = 1; 就成了正常代码,第 12 行的 */ 成了没人认领的符号。
(c) 会执行。 这是这道题最反直觉的地方。
int a = 1; 位于第 11 行,正好在"第 10 行注释关闭"之后、"第 12 行报错"之前。编译器把它当成一句普通的、完好的赋值语句处理了。
想亲眼验证的话,把第 12 行那个多余的 */ 删掉,只留前 11 行,F5,控制台会打印出 1。
/* 第一段注释 /* 第二段注释 */ int a = 1; Console.WriteLine(a); // ← 输出 1(如果编译器连 int a = 1; 也一起忽略,那报错就不会出现在第 12 行了。)
(d) 两种改法都能编译:
用 // 逐行注释(推荐):
// 第一段注释 // 第二段注释 // int a = 1; Console.WriteLine(3);或者干脆把内层那对标记去掉,让外层注释包住全部:
/* 第一段注释 第二段注释 int a = 1; */ Console.WriteLine(3);第一种是日常写法。 第四节说过原因:逐行加 // 天然避开了 */ 提前闭合的问题。
第 3 题
三处不该写的注释:
注释 为什么多余
// 声明变量 int total = 0; 一眼就知道是声明并初始化。看不懂的人不是新手,是压根不该看这段代码
// 循环 for 关键字本身就是循环的说明书。写 // 循环 相当于给门牌上写"这是一扇门"
// 把 i 加到 total 上 这条最典型。total = total + i; 的意思用眼睛读一遍就懂,注释一个字的额外信息都没提供
另外 /* 输出结果 */ 也算半个废话——它没说出任何代码之外的东西,属于"复述"而不是"解释"。
判断标准给一条:注释应该提供代码本身给不了的信息。 凡是把代码翻译成中文念一遍的,删掉;凡是解释"为什么这么写"“这里为什么特殊”“这个数哪来的”,留下。
改完的版本:
using System; namespace HelloWorld; class Program { static void Main(string[] args) { int total = 0; for (int i = 1; i <= 100; i++) { // 从 1 加到 100 而不是从 0 开始:第 0 轮等于什么都没加,白跑一次 total = total + i; } Console.WriteLine(total); } }现在只留了一条注释,它回答了代码回答不了的问题——为什么从 1 开始而不是 0。这条删掉之后,后来的人真的会去猜。
如果 total 那两行再配一条说明"这里没用 += 是为了照顾还没学到运算符的读者",那就属于过度注释了。同一份代码,同一个人,注释密度应该跟着读者的水平走。
下一步
下一篇:《变量:给内存里的数据起个名字》
今天这十几行代码里,你已经能写出注释了。但有一个问题还没解决:Console.WriteLine(“A”); 里面那个 “A” 是死的,写什么就打印什么。如果你想打印用户输入的名字,那个名字从哪来、怎么先存起来再打印?
下一篇讲变量。int age = 18; 这一行会拆成四部分讲清楚——类型是什么、名字是什么、= 右边是什么、存进去之后这个 age 到底意味着什么。学完你能写出第一个能接收输入的程序。
关于版本说明
本文信息截至 2026 年 10 月。
- 文中所有界面、代码、报错信息基于 Visual Studio 2026(18.x) 与 .NET 10(LTS) 撰写,语言版本 C# 14。.NET 10 由微软支持到 2028 年 11 月 10 日。
- 截至写作时,最新 .NET SDK 为 10.0.4xx 系列(随 Visual Studio 2026 各版本发布),.NET 运行时为 10.0.11。Visual Studio 2026 从 18.0 起改为按月发布次版本,.NET SDK 仍按季度发布,两者不是同一节奏。
- 本文里的所有编译结果(CS1525、CS1591、生成 t1.xml)均在本机用 .NET SDK 10.0.401 实际编译验证,未使用推测的输出。
- 注释不嵌套的官方依据:C# 语言规范 — 词汇结构(Lexical structure),原文为 “Comments do not nest.”
- XML 文档注释完整说明:XML 文档注释(C# 编程指南)
- 快捷键对照:Visual Studio 键盘快捷键
- CS1591 的抑制方式与背景:使用 Swashbuckle 和 ASP.NET Core
如果你装的是 Visual Studio 2022,本文的代码、报错信息、四个快捷键、GenerateDocumentationFile 设置项完全一致,框架选 .NET 8.0 即可,生成文件的输出目录从 bin\Debug\net10.0\ 变成 bin\Debug\net8.0\。生成的那个 XML 文档文件名跟着你的项目名走。
两个可能随版本变化的地方:CS1525、CS1591 这类错误和警告的编号与描述文字是编译器对外承诺的接口,十几年没变过;而"生成包含 API 注释的文件"这个勾选项的具体位置,不同版本在属性窗口里的排布略有差别,找的时候在 生成 → 输出 这一栏找就行。
另外提一句:第六节那个"/// 贴错了位置不报错"的结论,是编译器当前不报警告,不等于官方承诺永远不报。写类库的时候还是把注释贴准了,工具链以后可能变严。