第一个程序:五行代码是怎么跑起来的(零基础入门 · 第 3 篇)
摘要C# 程序到底从哪一行开始跑把 Program.cs 里的 using、namespace、class、Main、Console.WriteLine 逐行拆开为什么必须写 static、string[] args 到底装的是什么、大括号和分号各管哪一段、编译器在你看不见的地方补了哪些代码。三个可运行的小实验验证每一行的作用一次讲清 CS1002、CS1513、CS1529 这些红字报错长什么样。文末 3 道练习题和参考答案。零基础入门 · 第 3 篇。难度★☆☆☆☆ 预计阅读 12 分钟上一篇你按了 F5屏幕上冒出 Hello, World!然后窗口停住等你按键。那时候你做的事情和一个从没写过代码的人完全一样——按了个按钮然后相信它。程序内部到底发生了什么一行都没看。这篇把那个黑盒打开。目标很具体你能看着一个 C# 文件从头到尾说出每一行在干什么、删掉它会怎样。 做到这一步你才算真的开始写代码而不是在抄代码。一、先把文件掏空只留 5 行打开你昨天建的项目双击解决方案资源管理器里的 Program.cs把里面原有的内容全部删掉然后原样敲下面这段using System; namespace HelloWorld; class Program { static void Main(string[] args) { Console.WriteLine(Hello, World!); } }敲的时候有个小细节当你打出 { 的时候Visual Studio 会自动帮你补上配对的 }光标停在中间那一行。你按回车换行就行别自己再手打一个大括号多打一个这篇后面会专门讲怎么排查。这段代码跑起来输出的东西和昨天那两行顶级语句完全一样一行 Hello, World!。现在你看这个文件其实就是三样东西套在一起一行引入using System;一层空间和一层壳namespace class Program一个入口方法 它要做的事Main Console.WriteLine数一数真正干活的行using、namespace、class、Main、Console.WriteLine正好 5 行。其余的都是大括号和空行。下面就按这 5 行的顺序一行一行拆。二、第 1 行using System;using System;术语解释using 指令using directive也叫 using 声明是写在文件最上面的一行引入说明。命名空间namespace是 C# 给类分组的抽屉名System 是最核心的那个抽屉系统里绝大多数基础类都装在里面。Console 这个类住在 System 抽屉里。C# 的规则是你写 Console编译器得知道去哪儿找它。抽屉没打开它就是不认识这个名字。using System; 就是打开这个抽屉。打开之后下面这些代码你都能直接这么写Console.WriteLine(可以了); DateTime.Now.ToString(); Array.Emptyint();不用变成 System.Console.WriteLine(“可以了”) 那种啰嗦写法。为什么昨天生成的代码里没有这一行因为 Visual Studio 2026 的项目默认开了隐式 usingImplicitUsings。编译器已经替你把这行加上去了。控制台项目会自动引入这七个using System; using System.IO; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks;System 在第一个已经用上了。手写这一行不会有任何问题也不会冲突——编译器发现重复引入同一个命名空间直接忽略。写上它的价值只有一个让你看得见程序需要什么。学初期建议保留这个习惯等哪天你在 WPF 项目里用 System.Windows.Controls忘了加 using 立刻就知道是怎么回事了。想验证这个说法很简单把 using System; 删掉再按 F5。程序照样跑。说明这行确实是多余的。但如果再往前加一句 File.ReadAllText(“a.txt”)File 类住在 System.IO删掉 using 就会立刻报错——System.IO 不在那七个隐式名单的自动引入范围里吗它在所以你还是不用写。只有用到名单之外的东西才需要补。记住一条规则using 必须写在文件最开头任何代码之前。放到中间会报 CS1529后面会讲。三、第 2 行namespace HelloWorld;namespace HelloWorld;这一行你项目名叫 HelloWorld所以生成的代码里就是这个名字。如果你项目叫 ConsoleApp1这里就是 ConsoleApp1。命名空间是干什么的 同名冲突的解决机制。现实里你电脑里可以同时有 订单/2024年.xlsx 和 报销/2024年.xlsx两个文件重名不打架因为路径不同。命名空间就是这条路径。订单.表格 和 报销.表格 是两个不同的东西。程序大了之后你可能会写一个叫 Config 的类别处也有一个叫 Config 的。它们各在自己的命名空间里谁也不干扰谁。注意这一行末尾没有大括号。 这是 C# 10 之后引入的文件范围命名空间file-scoped namespace写法——一行声明整个文件都属于这个命名空间。Visual Studio 2019 及更早生成的是老写法要带一对大括号namespace HelloWorld { class Program { ... } }两种写法编译出来的东西一样。老写法在大项目里更好用因为一个文件里能开好几个命名空间块文件范围写法更清爽一个文件只能有一个命名空间。教程里两种都会出现认得出来就行。顺手把 namespace 改成 namespace HelloWorld2F5程序照跑。名字随便取它不影响运行只影响将来别人引用你的类时怎么写。四、第 3 行class Programclass Program { ... }术语解释类class是 C# 里组织代码的基本单位。你可以理解成一个文件夹里面装着变量叫字段、方法叫函数和属性。类和对象的关系第 22 篇专门讲现在只需要知道类是一个装东西的壳。这一行做了两件事造了一个名叫 Program 的壳打开这个壳的大括号把里面的东西收进来Program 这个名字有讲究吗 在老写法里它就是约定俗成的默认名和 Main 配套出现。它不是关键字改成 MyApp 也能跑class MyApp { static void Main(string[] args) { Console.WriteLine(Hello, World!); } }只要那个叫 Main 的方法还在照跑不误。类名叫什么不重要方法名才是重点。大括号 { } 的作用是划定范围。编译器靠它数括号找到一个 { 就往下找配对的 }中间的全部算作这个块的内容。C# 编译器的世界是不讲缩进的。 你把整段代码压成一行它照样能编译。空格、换行、Tab 全是给人看的编译器一个都不看。所以配对大括号必须齐全一个都不能少——少了编译器就没法判断哪段代码归谁管。把 class Program 后面那个 { 删掉或者把 class Program 这个词整个删掉F5红色波浪线立刻出现错误列表里报的是错误 CS1022类型或命名空间定义或文件结束预期因为编译器读到 class Program 却找不到开头的 {不知道这个类从哪儿开始、到哪儿结束只能判定这里缺了东西。同一个错误也可能显示为 CS1513} 缺失具体显示哪一个取决于编译器扫到哪一步才决定自己缺了啥——这也是新手看报错懵的原因之一错误信息说的是我在这儿没看到我该看到的东西不一定直接点名你删掉的那一行。五、第 4 行static void Main(string[] args)这是全文最重要的一行也是最容易看不懂的一行。拆成四块看static void Main ( string[] args ) ① ② ③ ④① static —— 静态修饰符术语解释静态static表示这个成员属于类本身而不是属于某个具体的对象。你现在没有对象也没有学对象所以先记一条实用的理解static 加上之后你在 Main 里面可以直接写 Console.WriteLine不用先拿到一个 Console 对象再用。等第 22 篇讲类和对象的时候你会知道不加 static 意味着什么。在 Main 这里static 必须写。少了它编译不过错误 CS5001程序缺少入口点顺带说主方法Main方法缺少可编译的静态入口点这是新手第一次见入口点这个词的地方。② void —— 返回值类型术语解释返回值是方法做完之后交给外面的一份结果。Console.WriteLine 做完不交任何东西出来它的返回值类型就是 void空的、无。对比一下int x 5; // x 是 int交出 5 这个整数 void ShowText() // ShowText 是 void什么都不交出Main 是 void意思是这个方法跑完就算完事不给谁返回一个数。后来你会学到 static int Main(string[] args) 这种写法它交出一个整数程序结束时能被别的程序接收到。微软文档里管这个叫退出代码。现在先不管记住 void 就够了。③ Main —— 方法名这个是固定不能改的。Main 首字母大写的 M。编译器找程序从哪儿开始就找这个叫 Main 的方法。改成 main、改成 Main2、改成 我的入口都编译不过错误 CS5001程序缺少入口点整个程序只能有一个 Main。 两个也报错不过那是 CS0017程序只允许有一个入口点。④ (string[] args) —— 括号里的参数术语解释参数parameter是方法接收的外部输入。写在方法名括号里的那串东西就是参数列表。string[] args 拆成两个概念string —— 文本字符串第 10 篇专门讲[] —— 数组表示一大堆第 14 篇专门讲合起来 string[] 一串文本组成的数组。args 是这个参数的变量名args 是 arguments参数的缩写。它装的是什么程序启动时从命令行传进来的东西。你在命令提示符里敲dotnet run HelloWorld abc 123程序里那个 args 数组里就有两个元素“abc” 和 “123”。不传参数的话args 存在但它永远不为 null只是长度为 0。所以下面这段代码不会崩foreach (var arg in args) { Console.WriteLine(arg); }你按 F5 的时候 Visual Studio 没给你传任何参数所以这个循环一次都不执行什么都不打印。这段代码现在可以先跳过第 17 篇讲方法、第 19 篇讲 foreach 之后再回来读。 你只要知道它是个装着启动参数的空篮子。六、第 5 行Console.WriteLine(“Hello, World!”);前面四行都是铺垫这一行是真正干活的那一行。它做了两件事第一把 Console 这个抽屉打开那是第 1 行的功劳。Console 类代表控制台——就是那个黑色的、有光标的命令窗口。第二调用它的 WriteLine 方法把括号里那句英文打到窗口上。三段连起来读部分 是什么Console 一个类代表控制台这个对象. 点号从一个东西里面取出另一个东西这里是取出 Console 里的 WriteLine 方法WriteLine 方法名Write Line打印一行(…) 传给方法的数据“Hello, World!” 一段文本。两边的英文双引号是 C# 表示这是一段文本的方式; 分号这条语句到此结束Write 和 WriteLine 的区别Console 家族里有两个孪生兄弟Console.Write(“你好”) —— 打完之后光标不换行下一个 Write 接着同一行后面打Console.WriteLine(“你好”) —— 打完之后自动换到下一行试一下把整段代码替换成下面这份完整照敲using System; namespace HelloWorld; class Program { static void Main(string[] args) { Console.Write(第一段); Console.Write(第二段); Console.WriteLine(); Console.WriteLine(换行之后的第三段); } }F5输出是第一段第二段 换行之后的第三段看明白了吗Write 把前两段挤在同一行然后一个不带参数的 WriteLine() 负责换行空格和右括号之间什么都没有它就是单纯换一行。再敲一次对照实验把上面代码里的第一个 Console.Write 改成 Console.WriteLineConsole.WriteLine(第一段); Console.WriteLine(第二段);输出变成第一段 第二段括号里那个 ! 有问题吗“Hello, World!” 里的感叹号是文本的一部分不是 C# 语法。C# 里表示逻辑非的那个 ! 符号只有单独出现在代码位置时才有特殊含义在双引号里面它就是个普通字符。双引号里的东西永远只是文本编译器一个字符都不解析。你可以在里面写 11、写 Console.WriteLine、写中文符号都行。七、大括号和分号两种收尾符号你在上面已经见过它们但值得单独拎出来因为新手 90% 的红字报错来自这两样东西。分号 ; —— 句号C# 不认换行。编译器不靠这一行结束了来判断语句结束它只认分号。Console.WriteLine(A); Console.WriteLine(B);写成这样编译器看到的是两个分号 → 两条语句。写成这样Console.WriteLine(A); Console.WriteLine(B)分号只有一个 → 编译器认为这是一条还没结束的语句然后往后继续找找到文件结尾也没找到第二个分号于是报错错误 CS1002需要; (运算符或分隔符)把两个 WriteLine 之间的换行去掉压成一行、只保留一个分号错误一模一样。这就证明了分号才是真正的结束标记换行只是排版。最后一个语句后面也要分号别漏。大括号 { } —— 块的边界{ 表示从这里开始是一个块} 表示这个块到这儿结束。符号 管什么范围( ) 方法的参数或者表达式的计算范围{ } 代码块一批语句的整体[ ] 数组的下标或取值你今天看到的是两个 {一个属于 class Program一个属于 Main 方法层层包含。第三对是 ( )管参数。VS 有个救命功能光标点在一个括号上和它配对的那个括号会变成灰色底。括号配对错乱的时候这招非常好使不用自己数。另外 Ctrl] 键可以让光标在配对括号之间来回跳。文件一大靠肉眼数括号会疯。三种括号配错的报错长相把第 4 行的 Main 后面那个 ) 删掉错误 CS1026) 缺失把 Console.WriteLine(“Hello, World!”); 这行前面的 { 删掉错误 CS1513} 缺失注意这个报错的位置很坑人——它报在文件最后一行不是报在真正缺括号的地方。编译器只知道我扫到文件末尾了还有 { 没关上它不知道你是在哪一行丢的。所以下次看到 } 缺失别在最后一行找往上翻。八、按 F5 之后程序内部到底发生了什么从你敲 F5 到屏幕出现字中间是这么一条流水线① 编译CtrlShiftB 就能单独做这一步Visual Studio 把你写的 .cs 文本交给编译器Roslyn。编译器逐字符扫描检查语法——括号配不配对、分号在不在、关键字拼对没拼对。只要有一个语法错误这一步就停下屏幕不会弹窗红波浪线标记出错的那一行同时底部的错误列表面板把所有错误列出来。双击列表里的任意一条光标就跳到那一行。这一步的结果是一个 .exe 可执行文件躺在你项目文件夹下的 bin\Debug\net10.0\ 里框架版本号跟着你选的框架变.NET 8 项目就是 net8.0。C# 是编译型语言源码不能直接跑必须先翻译成机器能执行的指令。这一点和 Python、JavaScript 那类边解释边跑的脚本语言不同——所以 C# 的语法错误在运行之前就被拦下来了不会出现跑到第三行才崩的情况。② 启动编译通过Visual Studio 弹出黑色控制台窗口操作系统把那个 .exe 装进内存运行时.NET Runtime给它准备好所有基础环境。③ 找入口运行时在程序里找 Main 方法找到从它的第一个大括号开始一行一行往下执行。Console.WriteLine(…) 被调用 → 文本被写到控制台窗口 → 光标跳到下一行。④ 结束Main 方法的花括号闭合整段代码执行完方法返回程序退出。CtrlF5 和 F5 的区别上一篇提过这里说清楚原因F5 启动后程序跑完控制台窗口停住等你按键这是调试模式的刻意设计避免你看不到输出。CtrlF5 是启动但不调试跑完立刻关窗口。这两种启动方式走的是完全相同的编译和执行流程区别只在有没有调试器挂在旁边。语法错误在两个快捷键下一样都弹不出来。九、编译器替你补了什么现在回答一个问题为什么昨天那两行的顶级语句也能在控制台上打出 Hello, World!它明明没有 Main。因为 顶级语句Top-level Statements这个特性背后编译器补了一整套壳子。你写的是方法体剩下的它自己造你写的 Console.WriteLine(Hello, World!); 编译器生成的 [自动加的类 Program] [自动加的入口方法签名 static void Main(string[] args)] Console.WriteLine(Hello, World!);微软文档里有句话值得记“生成的方法的名称不是 Main”——编译器生成的那个入口方法确实叫 Main但它内部用的名字另有安排这是代码无法直接引用的实现详细信息。你不用管它只要知道入口点在程序就能跑。入口方法的签名是编译器按你的代码自动挑的你的代码里有 编译器生成的入口签名既没有 await 也没有 return static void Main(string[] args)有 return static int Main(string[] args)有 await static async Task Main(string[] args)await 和 return 都有 static async Task Main(string[] args)你现在写的是第一种和第 4 节那份老写法一模一样。所以两种写法跑出来的东西完全相同。另外两条规则现在知道就行一个项目里只能有一个文件包含顶级语句用了顶级语句的项目就不能再自己写一个 Main 来当入口点写了也不生效想亲眼看看编译器补出来的样子在项目里添加一个新类写一个叫 Hello2 的方法和一个叫 Test 的方法进去然后不要在 Program.cs 里调用它们而是把 Program.cs 全部内容替换成Hello2.Test();F5 能跑通。说明你的类就在这个项目里编译器全都看得见——类不写在 Program.cs 里不代表不存在。这条第 22 篇还会展开。十、三个可以立刻做的小实验实验 A验证 static 是不是必须把第 4 行的 static void Main 改成 void MainF5。错误 CS5001程序缺少入口点程序缺少入口点——这五个字的意思是运行时找了一圈没找到启动的地方。原因是不加 static 的方法不能被当入口用。改回来加上 static又好了。实验 B验证分号才是结束标记Console.WriteLine(第一行); Console.WriteLine(第二行); Console.WriteLine(第三行)注意最后一行没有分号F5错误 CS1002需要; (运算符或分隔符)把最后那个分号补上三行全出来。再把三个语句压成一行、分号都留着输出完全一样。实验 C验证 args 真的是空的Console.WriteLine($参数个数{args.Length}); foreach (var arg in args) { Console.WriteLine($我收到了{arg}); }$“…” 是插值字符串——字符串前面的 $ 表示可以用 {} 把变量的值嵌进来第 10 篇会细讲这里只需要看懂。F5 输出参数个数0一个参数都没有第二段循环一次都没进。 这就是 args 的真相Visual Studio 用 F5 启动时不给你传参数。想看到它装东西的样子用命令行跑在项目文件夹上按住 Shift 右键 → 在此处打开 PowerShell 窗口输入 dotnet run 苹果 香蕉 5回车输出变成参数个数2 我收到了苹果 我收到了香蕉 我收到了5args 数组存的就是你在命令行敲的那一串东西顺序不变。 你现在能说清它是什么了。十一、常见错误汇总这一节把新手第一天会撞的红字全部列出来。报错信息长什么样和怎么改同样重要——看到报错时要能一眼认出是哪一类。CS1002需要; (运算符或分隔符)漏分号。检查上一行末尾或者检查当前行末尾。CS1513} 缺失大括号不配对。报错位置在文件末尾不代表那里真有问题往上翻。VS 里把光标点在每个 { 上看配对的 } 是不是灰色的能快速定位。CS1026) 缺失圆括号不配对。常见于 WriteLine( 打了一半就去换行了。CS1519类、结构或接口成员声明中的修饰符“xxx”无效关键字用错了地方。比如在 class Program 的大括号外面写 Console.WriteLine(…)编译器会把 Console 当成一个修饰词于是报这条。它长得吓人实际意思只是你这个位置不该出现这个东西。CS1529using 子句必须位于命名空间中其他元素之前using 没写在文件最开头。比如下面这样就会报错namespace HelloWorld; // ← 命名空间在前 using System; // ← using 在后报错CS5001程序缺少入口点三种可能1Main 拼错了2static 漏了3整个 Main 方法被删了。CS1022类型或命名空间定义或文件结束预期多写了一个 }。VS 自动补的大括号 你手打的重复了。红波浪线出现了但程序还能跑不可能。语法错误编译不过程序一定跑不起来。如果你遇到上次还能跑这次不行那就是这次真的改坏了对着错误列表从第一条开始逐条修别只改第一条——一个漏分号可能引出七八条连带错误改完第一条后面一片就没了。改动没生效改了代码按 F5跑出来还是老的。99% 是你改的不是当前运行的程序。检查解决方案资源管理器里加粗显示的那个项目名——加粗的才是启动项目。右键项目 → 设为启动项目再试。练习题今天三题都在 Program.cs 上动手答案不需要写代码逻辑重点是把结构说清楚。第 1 题你把 Program.cs 改成下面这样class Program { static void Main(string[] args) { Console.WriteLine(Hello, World!); } }比正文里少了一行 namespace HelloWorld;多了一行 using System;位置也不一样。F5 之后程序正常跑起来了输出了 Hello, World!。请回答a删掉 namespace HelloWorld; 为什么没报错这一行到底影响什么b删掉 using System; 为什么也没报错它在什么情况下才会变成必须的第 2 题下面这段代码会报一个错using System; namespace HelloWorld { class Program { static void Main(string[] args) { Console.WriteLine(A) Console.WriteLine(B); } } }请回答a完整写出报错信息的编号和中文描述。b编译器报出的错误位置在哪一行真正出问题的是哪一行这两者为什么不一样c修好之后如果想故意制造 CS1513你会怎么改第 3 题有人告诉你「C# 里 Main 方法可以随便改名反正程序找的是入口点。」请你根据今天学的内容判断这句话对不对并回答aMain 能不能改成 main改成 main1 呢各会报什么错b既然方法名不能改那 Main 后面括号里的 string[] args 能不能改成 int[] args或者干脆写成 ()各会怎样cargs 数组本身在 Visual Studio 里按 F5 运行时里面有几个元素为什么它不会因为没有内容就报空引用错误参考答案第 1 题a没报错是因为命名空间不是编译器要求的。Main 是不是入口点靠的是「public/static/void/括号里 string[]」这一整套签名规则跟它待在哪个命名空间里没关系。namespace 影响的是将来别人怎么引用你的类。现在这个项目只有它自己用类名写全不带前缀也没人管。等你写到第 22 篇有了自己的类库、或者第 56 篇开始做 WPF 项目一个类被两个项目引用时命名空间就决定了你写 使用方.你的命名空间.你的类 还是 使用方.你的类。少了它不是编译错误是以后的麻烦。busing System; 也没报错是因为项目默认开了隐式 using编译器已经自动引入了 System。真正的 Console 类还在那儿名字还能认出来。什么时候变必须当你用到不在自动引入名单里的东西的时候。控制台项目自动引入的是 System、System.IO、System.Collections.Generic、System.Linq、System.Net.Http、System.Threading、System.Threading.Tasks 这七个。比如你要用 System.DrawingWinForms 里画图用的那个 Color、Point 都在这儿它不在这七个里手上又没有 using System.Drawing;就会报错误 CS0103当前上下文中不存在名为Color的名称判断口诀报不存在于当前上下文的名字先去搜它住在哪个命名空间在文件顶上补一行 using。第 2 题a完整报错错误 CS1002需要; (运算符或分隔符)漏的是 Console.WriteLine(“A”) 这一行末尾的分号。第二行有分号所以编译器把两行当成了同一条还没结束的语句然后一路读到文件结尾也没等到分号。b报错位置在文件最后一行或者 CS1002 标记在缺少分号的那一行末尾。真正出问题的是 Console.WriteLine(“A”) 那一行。两者不一样的原因是编译器是顺序扫描的。 它读到第一行末尾发现没有分号就知道这条语句还没完于是把后面所有内容都当成这条语句的延续继续读。读到文件末尾发现还是没分号才确定缺了一个。所以它报在它发现缺口的时刻而不是缺口实际发生的位置。这是新手最容易被误导的地方——看到文件末尾报错就死盯着最后一行看其实错在开头。看到行号靠后的报错第一反应应该是往上翻。c制造 CS1513} 缺失把 Main 方法的闭合大括号删掉一个using System; namespace HelloWorld { class Program { static void Main(string[] args) { Console.WriteLine(A); Console.WriteLine(B); // ← 这里把 Main 方法的那个 } 删掉了 } }编译器扫到文件结束发现还有一个 { 没关上报 CS1513} 缺失并且报错位置显示在文件最后一行。这正好印证了b里说的规律。第 3 题a这句话是错的。 Main 不能改。改成小写 main → 报错 CS5001程序缺少入口点。运行时找的是区分大小写的 Mainmain 它不认。改成 main1 → 同样报 CS5001理由一样。改成 我的入口 → 中文标识符在 C# 里合法编译本身没问题但依然报 CS5001。微软文档里也讲得很直白入口点是靠 Main 这个特定名字 一整套签名共同确定的。你可以改类名正文里把 class Program 改成 class MyApp 照样跑但 Main 这个方法名动不得。b括号里的东西也不能随便改。string[] args 不能改成 int[] args。编译器报错错误 CS5001程序缺少入口点因为 C# 规定主方法的参数必须是 string[] 或者 string[] args 的等价形式历史上还允许无参 Main()但那是早期 C# 的写法了官方文档现在的主入口点标准就是 string[] args。写成 int[] args签名不匹配编译器认定这不是一个合法的入口点。能不能写成空的 ()要看项目设置。 默认的 下static void Main() 单独存在会报 CS5001某些老项目为了兼容会开相关设置让它通过但这属于历史包袱不建议在教程里碰。结论入口点的签名基本是写死的——static、void或 int/Task 变体、Main、string[] args四个部分各有各的要求改哪个都不行。这个改不了是特性不是限制正因为它是固定的编译器才能在几万个类型里准确无误地找到程序该从哪儿启动。cargs 的长度是 0。Visual Studio 按 F5 启动时等价于在命令行执行 dotnet run后面不带任何参数所以 args 是一个长度为 0 的空数组。它不会报空引用错误NullReferenceException原因有两层微软文档明确写了 “args 变量从不 null”。运行时会给你一个真实的空数组对象不给你 null。就算拿到一个长度为 0 的数组遍历它也只是循环零次直接跳过不崩。只有当你自己写了 int[] nums null; 这种明确把变量设成 null 的代码去遍历它才会抛 NullReferenceException。数组没初始化 ≠ 数组是 null这是两个不同的状态。所以你写 if (args.Length 0) 来判断用户有没有传参数是安全的写法永远不用先判断 args ! null。这条习惯值得在所有地方保持数组和集合用 Length/Count 判断长度不要习惯性先判 null。下一步下一篇《注释写给人看的说明》今天这 12 行代码每一行都有明确的语法含义但你已经能看出问题在哪了——代码只说了做了什么没说为什么这么做。三个月后回头看你会知道 Console.WriteLine 在打印但完全不记得为什么这里要加一个 ReadKey、为什么项目名不能用中文。下一篇讲 // 和 /* */ 两种注释写法什么时候该写注释、哪些注释写了等于没写以及怎么让 Visual Studio 自动生成规范的文档注释。学完你写出的代码别人才看得懂。关于版本说明本文信息截至 2026 年 10 月。文中所有界面、代码和流程基于 Visual Studio 202618.x 与 .NET 10LTS 撰写语言版本为 C# 14。.NET 10 由微软支持到 2028 年 11 月 10 日属于长期支持版本。截至写作时最新的 .NET SDK 是 10.0.400随 Visual Studio 202618.9 一同发布。Visual Studio 2026 从 18.0 起改为按月发布次版本.NET SDK 仍按季度发布两者不是同一节奏。C# 14 / .NET 10 新增了基于文件的应用file-based apps可以不建项目文件直接 dotnet run hello-world.cs 运行单个文件。相关内容今天没展开因为本系列从头到尾走的是基于项目的传统流程VS 里建项目、跑 F5 的操作不受影响。顶级语句机制说明C# 控制台应用模板生成顶级语句顶级语句完整规则顶级语句 — 不使用 Main 方法的程序程序结构总览C# 程序的常规结构快捷键对照Visual Studio 键盘快捷键如果你装的是 Visual Studio 2022本文所有代码、报错信息、快捷键完全一致只是框架选 .NET 8.0生成的文件里顶级语句模板一样是默认的。生成可执行文件的输出目录从 bin\Debug\net10.0\ 变成 bin\Debug\net8.0\其余不动。唯一一处需要留意的历史差别namespace HelloWorld; 这种文件范围命名空间写法是 C# 10 才有的。如果你用更老的 VS比如 VS 2019 配 .NET Framework 项目生成的会是带大括号的老写法 namespace HelloWorld { }含义完全相同不要以为看错了。微软更新频繁你看到更高的版本号也不用担心——这几个错误代码和语法规则十年内不会变变的只有生成模板的默认选项。

相关新闻

西邮liiux兴趣小组2025一面题题解

西邮liiux兴趣小组2025一面题题解

西邮linux兴趣小组2025一面题题解 总结 本次一面题考点包含: C语言 printf 返回值C语言值传递、指针传递sizeof 、 strlen 、数组退化数组指针二级指针指针加减运算按位与运算三元运算符数组越界、未定义行为union 联合体柔性数组(零长数组)函…

2026/10/14 19:39:50 阅读更多 →
P1090 NOIP 2004 提高组 合并果子 题解

P1090 NOIP 2004 提高组 合并果子 题解

P1090 NOIP 2004 提高组 合并果子 题解 题目传送门: <{[()]}> 这题很容易想到一个贪心&#xff1a;每次取重量最小的两堆果子&#xff0c;再取合并后重量最小的两堆果子&#xff0c;直到只有一堆果子为止。 接下来是证明 证明贪心 假设现在只有 3 堆果子&#xff0c…

2026/10/14 19:39:50 阅读更多 →
Java多数据源事务配置:4种落地方案与本地消息表实战

Java多数据源事务配置:4种落地方案与本地消息表实战

1. 项目概述&#xff1a;为什么“Java多数据源事务配置”是每个中高级后端绕不开的硬骨头“Java多数据源的事务配置”——这八个字背后&#xff0c;藏着无数人在分布式系统演进过程中踩过的深坑、改过的凌晨三点的配置、回滚失败后焦灼盯屏的十几分钟&#xff0c;以及生产环境里…

2026/10/14 19:38:49 阅读更多 →

最新新闻

权威测评!2026年最火AI论文工具榜单,免费款也能高效产初稿

权威测评!2026年最火AI论文工具榜单,免费款也能高效产初稿

一、实测方法论&#xff1a;5 大维度量化评估 本次实测覆盖全学科论文场景&#xff0c;重点考察以下维度&#xff0c;结果客观可复现&#xff1a; 表格 评估维度权重测评标准中文适配度25%是否符合 GB/T7714 引用规范&#xff0c;语言逻辑自然度&#xff0c;术语准确性免费功能…

2026/10/14 20:21:39 阅读更多 →
Linux嵌入式SD/MMC驱动调试:信号流向、三层架构与实战排错

Linux嵌入式SD/MMC驱动调试:信号流向、三层架构与实战排错

1. 项目概述&#xff1a;为什么一个SD/MMC驱动分析值得花时间深挖&#xff1f;在嵌入式Linux开发一线干了十多年&#xff0c;我经手过的板子从ARM9、ARM11到Cortex-A7/A53/A72&#xff0c;再到如今的RISC-V SoC&#xff0c;几乎每一块主控芯片的BSP包里&#xff0c;SD/MMC子系统…

2026/10/14 20:21:39 阅读更多 →
最小二乘法全攻略:从线性回归原理到代码实现与避坑实践

最小二乘法全攻略:从线性回归原理到代码实现与避坑实践

我第一次真正用上最小二乘法&#xff0c;是在处理一组传感器校准实验数据的时候。当时手里是一堆带噪声的采样点&#xff0c;教科书里的公式能帮我“找一条最合理的直线”&#xff0c;但工程上真正让人头疼的&#xff0c;其实是后面这些事&#xff1a;数据里混了离群点怎么办&a…

2026/10/14 20:21:39 阅读更多 →
大模型应用落地指南:从模型选型到Agent与RAG实践

大模型应用落地指南:从模型选型到Agent与RAG实践

1. 模型维度&#xff1a;基座能力的格局与选型逻辑距离大模型真正进入大众视野到现在&#xff0c;已经过了好几个年头。站在2026年10月这个时间窗口往回看&#xff0c;最明显的变化是&#xff1a;市面上能叫得出名字的大模型&#xff0c;已经从早期的"百家争鸣"收敛成…

2026/10/14 20:21:39 阅读更多 →
Linux MMC驱动深度解析:SD卡识别流程与SDIO协议栈实战

Linux MMC驱动深度解析:SD卡识别流程与SDIO协议栈实战

1. 项目概述&#xff1a;这不是教科书里的驱动理论&#xff0c;而是现场拆解一张SD卡如何被Linux真正“认出来” 你有没有遇到过这样的情况&#xff1a;插上一张SD卡&#xff0c;系统日志里刷出一串 mmc0: new high speed SDHC card at address 0001 &#xff0c;但紧接着 d…

2026/10/14 20:21:39 阅读更多 →
从Zernike波前到PSF/MTF:光学像质仿真完整指南

从Zernike波前到PSF/MTF:光学像质仿真完整指南

做光学系统设计的人应该都有过这种体验&#xff1a;镜头草图画完、光线追迹也跑通了&#xff0c;但别人问"那成像到底清不清楚、能分辨多小的细节"&#xff0c;你一时却拿不出一个直观的答复。光线追迹给的是光路&#xff0c;不是像质。真正用来回答"成像多锐利…

2026/10/14 20:20:38 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起&#xff1a;它到底想解决什么问题第一次看到"AnyPS5"这个标题&#xff0c;我脑子里冒出来的第一个念头是&#xff1a;这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述&#xff1a;Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师&#xff0c;我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性&#xff0c;实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景&#xff0c;也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间&#xff0c;确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中&#xff0c;这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介&#xff1a;基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码&#xff0c;面向计算机相关专业课程设计与期末大作业学生&#xff0c;以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程&#xff0c;…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程&#xff1a;键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化&#xff0c;十个新手有八个栽在"往输入框里填东西"这件事上&#xff1a;要么填不进去&#xff0c;要么填了一半&#xff0c;要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程&#xff1a;阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀&#xff1a;什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面&#xff0c;跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 7:53:34 阅读更多 →