摘要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 帮你写不用背。光标放在要写说明的成员上按 CtrlShiftC菜单路径编辑 → 高级 → 添加 XML 文档注释VS 自动插好骨架并把光标放进 和 中间直接打字就行。你也可以手动敲三个斜杠VS 同样会自动补全标签。一个重要实验为什么你的控制台项目没报警告很多教程说写了 /// 却收到警告 CS1591但你现在的项目加了两行 ///构建面板干干净净0 个警告 0 个错误这不是你写对了是你的项目压根没让编译器去管这件事。想亲眼看到 CS1591照着做在解决方案资源管理器里右键项目名 → 属性顶部的下拉框从 活动配置 切到 所有配置找到 生成 → 输出 那一栏勾上 生成包含 API 注释的文件英文项目里是 Generate XML documentation file等价的做法是直接在 .csproj 里加一行PropertyGroup GenerateDocumentationFiletrue/GenerateDocumentationFile /PropertyGroup注意类名默认不写修饰符就是 internal程序集内可见不叫公开可见。 官方文档明确说 CS1591 只针对公开可见的类型和成员。所以光开这个勾选项还不会报。再把 Program 前面补一个 publicusing System; namespace HelloWorld; public class Program { public static void Main(string[] args) { Console.WriteLine(A); } }现在 F5或者 CtrlShiftB 只编译警告列表里出现warning CS1591: 缺少对公共可见类型或成员“Program”的 XML 注释 warning CS1591: 缺少对公共可见类型或成员“Program.Main(string[])”的 XML 注释最后 bin\Debug\net10.0\ 目录下会多出一个 t1.xml跟你项目名同名的那个。打开它里面就是编译器把你的 /// 转成的结构化文档?xml version1.0? doc assembly namet1/name /assembly members /members /docmembers 是空的因为你把 /// 删掉了。这就是 /// 的真正去处——它不是写给编辑器看的是写给文档生成工具看的。那要不要开这个设置控制台项目、练手项目不用开也没必要开。 你是在学语法不是在发布类库。写给别人用的类库、需要生成 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报错报在第几行真正出问题的是第几行cint 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 202618.x 与 .NET 10LTS 撰写语言版本 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 注释的文件这个勾选项的具体位置不同版本在属性窗口里的排布略有差别找的时候在 生成 → 输出 这一栏找就行。另外提一句第六节那个/// 贴错了位置不报错的结论是编译器当前不报警告不等于官方承诺永远不报。写类库的时候还是把注释贴准了工具链以后可能变严。