简介面向C#初学者的“C#100例”是一份以100个编程实例串联基础语法、面向对象、集合、异常处理、LINQ、异步编程、多线程、反射、Windows Forms/WPF及网络编程等核心模块的教程资源旨在帮助零基础读者从基本语法逐步过渡到实际项目开发建立扎实的编程思维。资源包共1717个文件压缩后约9.65MB主要包括224个cs源码、82个sln解决方案、167个exe示例程序、97个resx资源文件以及135个ico图标和163张jpg图片sln可直接打开对应工程exe可运行观察效果cs代码便于逐行对照调试整体按示例编号组织检索方便。目前已有490人学习/下载。每个示例覆盖一个独立知识点并配有完整的工程结构与界面资源读者可先运行exe感受程序行为再阅读源代码理解实现原理最后修改代码验证自己的猜想。从控制台应用到桌面UI、从同步到异步、从单机到网络通信覆盖不同层次的学习路径相比纯理论教程这种“运行—阅读—修改”的闭环方式能显著提升学习效率适合C#自学入门、课堂教学配套练习或考前集中刷题使用。1. C#入门为什么绕不开100例从语法到能写工具的最近路径很多想学C#的朋友一开始满网找教程市面上的C#教程要么厚得像字典要么讲半天环境搭建还没敲到第一行代码。我自己的经验是C#上手最快的方式不是从头到尾读语法而是跟着一套覆盖面全的实例集一边敲一边理解。这个C#100例资源就是这样的东西——100个可直接运行的例子从控制台语法、WinForms界面、文件操作到数据库访问几乎把初学者前三个月会碰到的知识点全占了。适合完全零基础的人按顺序练也适合写过一点但没做过完整项目的人快速补短板更合适把例子改造成自己小工具的上班族。2. 先看资源结构100个例子的分类逻辑与学习路线2.1 三类划分语法、界面、进阶应用拿到这套资源的第一步不是打开第一个例子而是翻目录。100个例子如果按编号一个个刷很容易在某个无聊的语法例子上耗尽耐心。我习惯先把它们分成三类分类数量占比典型内容建议用时基础语法约30%变量、运算符、循环、数组、方法1-2周WinForms界面约40%窗体控件、事件、菜单、对话框2-3周进阶应用约30%文件、正则、数据库、多线程、GDI3-4周这个划分不需要太精确重要的是理解三类例子各自的定位。基础语法类是积木单个例子小跑起来快重点是把怎么写变成条件反射。资源里前十几个例子基本是控制台输出和变量运算看着简单但手速和语感就是靠这些重复练出来的。WinForms类是模型房每一个例子都是一间能住的房间开始涉及控件属性、事件委托和界面刷新这些真实开发里的基本概念。到这一阶段代码量会突然增加一个窗体初始化就是十几行不用怕设计器会自动生成关键看事件方法里写的业务逻辑。进阶应用类是装修队文件读写和数据库访问是后面所有小工具的地基。这一块有少数例子开始涉及多线程和网络相对难啃但不用每个都吃透先把文件、数据库、正则这三个高频场景学会剩下的等真用到再回头看。三类之间不是严格隔离的。比如读取文本文件这个例子既可以用控制台程序演示也可以做成一个带打开文件对话框的窗体程序。我一般建议看例子标题时多看一眼它用的项目类型同一个功能在控制台和窗体里实现需要额外处理的边界完全不同。控制台版本只需要搞清楚流的方向窗体版本还要考虑用户选文件、进度显示、界面释放这些额外因素。2.2 学习顺序控制台先行别被界面骗很多初学者一上来挑WinForms的例子因为看着有窗口、有按钮感觉自己在做软件。结果控件属性、事件委托、界面布局三个概念同时涌过来很容易在第一个窗体的设计器里迷路代码逻辑还没跑通先被一堆可视化操作的细节淹没了。最常见的翻车方式是照着视频把控件拖好、属性设好代码部分反而没理解最后关掉视频自己写就卡住。我的排序建议是严格走控制台 → WinForms → 数据库文件三步。控制台阶段把基本语法过完重点关注三类高频问题值类型和引用类型的区别、字符串拼接与格式化、循环里break和continue的作用。这三个问题在100个例子里会反复出现在控制台阶段理解透了后面无论做窗体还是做服务都很顺手。WinForms阶段的核心不是记住每个控件的属性和方法而是抓住事件驱动这个模型。界面上放一个按钮双击它就能生成一个事件处理方法这个方法的签名是固定的(object sender, EventArgs e)理解了这个签名大部分控件事件都能照着写。再往后看定时器、列表选择变化、鼠标键盘事件其实都是同一个套路只是触发时机不同。数据库和文件操作放在最后是因为它们依赖前面的界面知识。用DataGridView绑定数据时需要先知道数据表长什么样做登录窗口时需要先会写文本框的事件。把顺序反过来每一步都要回头补前面的概念学习曲线会陡很多而且容易产生控件事件还没搞懂怎么又来一堆数据库对象的挫败感。时间分配上我建议每天做三到五个例子每个例子做完不要急着看下一个先在原代码基础上改一个需求。比如例子是把一行文本输出到窗体改成在多个文本框里按格式拆分输出比如例子是读取文本文件并显示行数改成读取文本文件并按行倒序显示。语法没变但思考量上去了这是从看懂到会用的分水岭。2.3 环境准备项目类型和框架版本怎么选这套资源里的例子大多基于较老的开发环境编写默认项目模板是.NET Framework的Windows窗体应用。现在用新版IDE新建项目时对话框里会同时出现Windows窗体应用和Windows窗体应用(.NET Framework)两个模板前者用的是.NET Core/.NET 5后者才是老例子能直接跑起来的那个。我的建议是练习阶段一律选.NET Framework模板目标框架选4.x。这样有两个明显的好处第一老例子里的命名空间和控件引用方式不用改编译通过率高第二这个资源对应的答疑和资料大多基于老框架排查报错时更容易匹配上。如果机器上只有新版环境也可以把老代码迁移过去但部分例子中System.Drawing、System.Data的引用方式会有细微差别新手容易卡在命名空间不存在这类报错上。注意判断例子的目标框架最直接的方法是看项目文件里有没有packages.config或者打开项目的属性页看目标框架下拉框。老例子里常见的TargetFrameworkVersion值在新环境里不一定默认支持。老例子解压出来一般是一个个单独的文件夹每个文件夹对应一个完整项目里面是.csproj文件、Properties目录和Program.cs。直接用新版IDE打开.csproj通常会提示做一次提升转换点确定即可。但有些例子里引用了绝对路径的DLL提升后会编译不过这时要手动加引用。如果对项目文件不熟建议先挑一个最简单的例子走通整个打开流程再批量处理其他例子。环境这一步看着跟写代码无关却是照着敲却编译不过的头号原因。项目类型选错后面每一步都在跟IDE较劲而不是在学C#。选对了模板再打开第一个例子双击运行能看到一个窗体弹出来这个正反馈比什么激励都管用。3. 核心例子逐行拆解从窗体到数据库的四个实战原型3.1 第一个窗体程序按钮、事件与MessageBox资源里第一个WinForms例子通常非常简单窗体上放一个按钮点击弹出Hello World消息框。这个例子虽然短却能把事件驱动这个概念讲透也为后面所有带界面的例子打底。namespace Example001 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } // 按钮点击事件把一句话显示到Label控件上 private void BtnShow_Click(object sender, EventArgs e) { lblResult.Text 你好C#100例; MessageBox.Show(点击事件已触发, 提示, MessageBoxButtons.OK, MessageBoxIcon.Information); } } }代码里的逻辑分三段。构造函数负责把设计器里生成的控件初始化代码跑起来这段不用手写但要知道它存在。事件处理方法BtnShow_Click是按钮被点击时自动调用的回调方法名左边的private表示这个方法只在类内部使用右边的void表示没有返回值。方法内部两行第一行给Label赋值第二行弹出一个标准消息框。这里需要理解的关键点是sender和e这两个参数。sender代表触发事件的控件本身类型是object使用时需要先转换成Button才能访问按钮的其他属性e携带事件相关数据对于点击事件来说通常是空的。当同一个窗体上有多个按钮共用一个事件方法时可以通过((Button)sender).Name来区分是哪个按钮被点了这是老例子里非常常见的判分方式。3.2 文件读写StreamReader与编码问题文件操作是100例里最实用的一类几乎每个小工具都离不开。资源里的例子通常会同时演示写入和读取两个方向顺便把using语句、编码、路径三个知识点串在一起。// 写入文件StreamWriter追加模式 string logPath Path.Combine(Application.StartupPath, log.txt); using (StreamWriter writer new StreamWriter(logPath, true, Encoding.UTF8)) { writer.WriteLine(${DateTime.Now:yyyy-MM-dd HH:mm:ss} 登录成功); } // 读取文件按行读取到Liststring Liststring lines new Liststring(); using (StreamReader reader new StreamReader(logPath, Encoding.UTF8)) { string line; while ((line reader.ReadLine()) ! null) { lines.Add(line); } }用using语句包住StreamWriter和StreamReader是这套例子里反复出现的标准姿势核心目的是确保文件句柄在代码块结束时自动释放。不释放文件句柄的后果是文件被占用下次写入时抛文件正由另一进程使用的异常这个错在Windows上特别常见。构造函数里的三个参数分别是文件路径、是否追加写入、编码格式。追加模式传true时内容是接在文件末尾传false时会覆盖原文件。编码参数值得单独说老例子喜欢用系统默认编码但在简体中文系统上处理带中文的旧文件时用Encoding.GetEncoding(GB2312)读更稳自己写新文件时统一用UTF8避免在不同机器上出现乱码。文件路径这里埋了一个知识点Application.StartupPath拿的是程序exe所在的目录用Path.Combine拼接路径比手工加斜杠安全因为不用考虑路径分隔符在不同系统上的写法差异。如果文件在项目目录而不是输出目录运行时路径会不同这个边界经常让新手困惑。3.3 字符串与正则从文本里提取数据的通用套路第40个左右的例子开始接触正则表达式这是很多人第一次觉得C#难的地方。资源里的典型场景是从一段混合文本中提取电话号码看起来简单但写出来的pattern对新手很有冲击力。string rawText 联系人张三电话13800138000备用010-12345678; string pattern 1[3-9]\d{9}|\d{3,4}-\d{7,8}; foreach (Match match in Regex.Matches(rawText, pattern)) { Console.WriteLine($找到号码{match.Value}); } Console.WriteLine($共找到 {Regex.Matches(rawText, pattern).Count} 个号码);正则表达式的学习最忌背符号建议把pattern当成一个迷你语言来读。1[3-9]\d{9}表示第一位是1第二位是3到9后面跟着9个数字这是手机号\d{3,4}-\d{7,8}表示区号加座机号其中\d{3,4}表示三到四位数字{7,8}同理。中间的|是或者关系两个分支可以匹配不同形态的号码。C#里用前缀声明字符串反斜杠不用转义写\d就是\d这比在普通字符串里写\d省心很多。如果只想取第一个匹配用Regex.Match如果想替换用Regex.Replace。这三个方法在参数上高度一致第一个参数都是原始文本第二个都是pattern掌握一个就基本掌握了三个。实际开发里正则用得最多的是数据清洗场景比如从日志文件、报表文本里把时间、IP、错误级别提取出来再落到数据表里。常见踩坑点是正则引擎默认的贪婪匹配遇到提取字符串中两个引号之间的内容这类需求时容易把中间的所有内容都吞掉遇到这种情况先查一下贪婪和懒惰匹配的区别。3.4 ADO.NET数据绑定DataGridView与数据库表数据库相关例子的里程碑是把一张表显示到DataGridView里。这个例子统一了后面所有数据管理类小工具的写法也是初学者第一次接触到分层的数据访问对象。string connStr Data Sourcelocalhost;Initial CatalogDemoDB;User IDsa;Password123456; string sql SELECT Id, UserName, CreateTime FROM Users; using (SqlConnection conn new SqlConnection(connStr)) { SqlDataAdapter adapter new SqlDataAdapter(sql, conn); DataTable dt new DataTable(); adapter.Fill(dt); dataGridView1.DataSource dt; }这段代码的核心路径是SqlConnection负责建立连接SqlDataAdapter把SQL查询结果填充到内存里的DataTable最后把DataTable直接赋给DataGridView的DataSource属性。三个对象各管一段连接、查询、展示彻底分开这是老例子为了教学故意拆开讲的。新手最常犯的错是把SqlConnection、SqlCommand、SqlDataReader三个对象混着用其实展示场景下DataAdapter已经封装了查询和填充细节代码量更少也不容易遗漏释放操作。连接字符串可以看成一套键值对分号分隔。Data Source是数据库服务器地址localhost表示本机Initial Catalog是库名User ID和Password是登录凭据。如果是本机且用Windows身份验证可以把后两段改成Integrated SecurityTrue避免把密码硬编码在代码里。connect超时这个值默认15秒网络不稳时可以加Connect Timeout30。注意SqlConnection和SqlDataAdapter都实现了IDisposable用using包住它们是最稳妥的资源释放方式。老例子里有手工Close的写法一旦中间抛出异常Close可能不会执行到这个问题在实际项目里会造成连接泄漏。4. 经典算法与逻辑陷阱从排序、递归到LINQ4.1 冒泡排序的C#实现从数组到泛型List100例里排序是基本盘。冒泡排序在教科书里是最直观的排序算法但在C#里实现时有一个容易被忽略的细节数组的Length属性到List的Count属性遍历边界写法不一样。Listint numbers new Listint { 5, 3, 8, 1, 9, 2 }; for (int i 0; i numbers.Count - 1; i) { bool swapped false; for (int j 0; j numbers.Count - 1 - i; j) { if (numbers[j] numbers[j 1]) { // C# 7.0 元组交换老框架请换成三行temp写法 (numbers[j], numbers[j 1]) (numbers[j 1], numbers[j]); swapped true; } } if (!swapped) break; // 某一轮没发生交换说明已有序 }注意内层循环的边界j numbers.Count - 1 - i。每完成一轮最大的数已经沉到末尾下一轮不需要再比较它。这个边界是冒泡算法最容易写错的地方写成了Count会越界写成了Count-1会有一次多余比较。外层循环i Count - 1是因为只剩一个元素时不需要再排。如果目标框架比较老元组交换语法不支持要换成三行temp交换。swapped是一个优化标记。如果某一轮遍历里一次交换都没发生说明整个序列已经有序提前退出循环节省时间。对已经有序的输入这个优化能把时间复杂度从O(n²)降到O(n)。这套例子里排序只是载体真正要学的是比较和交换这个抽象。把if里的大于号改成小于号就得到降序排列把比较的规则提取成委托参数就能对自定义类型排序后面很多参数里都用到这个套路。4.2 递归与迭代斐波那契数列两种写法递归例子安排在排序后面主要是让学习者体会同一逻辑的两种表达方式。斐波那契数列是经典递归教材但直接照抄会踩性能坑// 递归写法简洁但性能差 static int FibRecursive(int n) { if (n 1) return n; return FibRecursive(n - 1) FibRecursive(n - 2); } // 迭代写法用循环代替递归 static int FibIterative(int n) { if (n 1) return n; int prev 0, current 1; for (int i 2; i n; i) { int temp current; current prev current; prev temp; } return current; }递归版本在n30时已经能感觉到卡顿n45以上就基本跑不动了原因是同一个子问题被重复计算了数万次比如FibRecursive(40)会递归调用大概上亿次。迭代版本用两个中间变量滚动前两个数每轮循环只做一次加法时间复杂度从指数级降到线性。如果硬要保留递归的写法可以用一张字典做记忆化把算过的子问题存下来但这已经是优化技巧而不是入门内容。学习这个对比的目的不是让你拒绝递归而是理解递归的适用边界。树形遍历、目录扫描这类天然有递归结构的问题适合递归因为代码和问题形状一致纯数值计算、顺序遍历的场景优先用迭代。很多开发者在面试时把递归当成高级技巧其实生产代码里递归用得很少反倒是迭代和循环更常见。例子集把这两个版本并列放在一起就是为了让你在最短时间内建立这个直觉。4.3 LINQ初体验查询语法与方法语法的区别100例里后期会出现LINQ这是C#相对其他主流语言比较突出的语言特性。理解LINQ需要同时看两种写法因为网上资料两种风格一半一半// 方法语法链式调用 var adults students .Where(s s.Age 18) .OrderByDescending(s s.Score) .Select(s new { s.Name, s.Score }); // 查询语法类SQL var adults2 from s in students where s.Age 18 orderby s.Score descending select new { s.Name, s.Score };方法语法和查询语法在编译后生成的是同一套中间代码选择哪种纯粹是风格问题。查询语法对从SQL转过来的人更友好尤其是where和orderby的位置和SQL很像方法语法在需要动态拼接条件时更灵活比如在循环里逐个添加Where条件。lambda表达式s s.Age 18是方法语法的核心左边是参数右边是返回布尔值的表达式编译器会根据上下文自动推断类型。遇到在集合中筛选的需求先想到LINQ而不是写for循环加if这是C#开发者和别的语言开发者一个显著的习惯差异。数据量只有几十条时两者性能没有差别但代码长度差一半可读性也差很多。LINQ还有一个好处是延迟执行上面的查询在用foreach遍历之前不会真正访问数据源。新手可能在这里遇到明明过滤了为什么结果没变的疑惑多半是因为执行时机没搞懂。5. 避坑照着例子敲代码最常见的五个翻车点100个例子本身就是一套完整的踩坑集合。以下五个问题是我在带着别人练这套资源时见到频率最高的基本覆盖了照着敲却跑不通的八成场景。5.1 现象运行报错“未将对象引用设置到对象的实例”新手照着窗体例子敲完一运行就在某一行抛NullReferenceException英文界面显示Object reference not set to an instance of an object。最常见的原因是设计器里的控件名和代码里引用的名字不一致。比如窗体上拖了一个button1代码里写的是btnShow.Click事件绑定对不上或者控件在InitializeComponent之前被访问。解决方法是先看错误信息定位到具体行号然后检查代码里引用的每一个控件名是否和设计器属性窗口里的Name一致。我习惯在InitializeComponent()之后的第一行代码打断点用快速监视查看this.Controls里的控件集合确认哪些控件已经创建了。还有一类情况是对象确实没赋值比如数据库查询返回了空结果却直接访问了行集合的第一行这种要养成先判断null再取值的习惯。5.2 现象窗体按钮点了没反应界面卡死执行一个耗时的文件操作或数据库查询时窗体会假死鼠标变成旋转圈点哪里都没反应。原因是代码跑在UI线程上阻塞了界面的消息循环。资源里老例子的处理方式往往很简单但实际用起来就会发现大文件操作时界面完全没法看。这背后的原理是Windows消息循环机制UI线程既要处理用户的点击又要执行你的代码两者共用一条线程。常见解决思路是把耗时操作放到后台线程。老例子里用BackgroundWorker组件新代码可以用Task.Run。涉及跨线程更新控件时要小心直接用赋值的方式更新文本会抛线程间操作无效的异常需要把更新动作切回UI线程比如用Control.Invoke。网上有人建议设置控件的CheckForIllegalCrossThreadCalls属性来屏蔽这个报错我一般不推荐那是掩盖问题而不是解决阻塞。5.3 现象数据库连接超时或打不开照着数据库例子改连接字符串本地连接没问题换一台机器就报在与SQL Server建立连接时出现与网络相关的或特定于实例的错误。原因通常是连接字符串里的服务器地址和实例名写死了换到别人的机器上服务名、端口、登录方式全都不一样。解决方法是把连接字符串放到配置文件里而不是写在代码里。在App.config中添加一个connectionStrings节点程序运行时通过ConfigurationManager.ConnectionStrings来读取。这样切换环境时只需要改配置文件不用重新编译。另外一个参数是连接超时时间SqlConnection默认15秒如果网络环境差可以设置Connect Timeout30。还要注意连接字符串的键值对用分号分隔如果密码本身带分号或不支持的字符需要用引号包住这种边界情况在真实项目里遇到过不止一次。5.4 现象用相对路径读写文件时找不到文件WinForms程序用相对路径data.txt去读文件运行时提示找不到文件但文件明明就在项目目录里。原因是工作目录不一定是程序集所在目录。在集成环境里按F5运行和直接双击exe运行时工作目录完全不同相对路径会基于不同的起点去解析。解决方法是不要依赖相对路径用Application.StartupPath或AppDomain.CurrentDomain.BaseDirectory来拼完整路径。另一个注意点是老例子的路径分隔符用的是\双反斜杠转义在新代码里最好用Path.Combine替代这样在Windows和跨平台场景下都不会出错。文件不存在时StreamReader会抛FileNotFoundException先试用File.Exists判断或者用try-catch包住这是例子代码里经常省略但生产代码里必须有的部分。5.5 现象编译报错“命名空间不存在”或“类型不存在”最常见的原因是目标框架版本不对。老例子里的某些命名空间比如System.Web.Extensions、System.Data.SqlClient在新框架版本里不是默认引用的。解决方法是在项目里检查引用列表右键项目选择添加引用在程序集列表里找到对应的项目勾选后重新编译。如果框架版本跨度太大有些老API已经被移除就需要找替代方案。给一个通用排查顺序先看项目目标框架是多少再看出错代码所在文件顶部有没有对应的using声明然后检查项目是否引用了对应的程序集。这三步按顺序走一遍八成的编译错误都能定位。还有一个看起来很奇怪但真实存在的坑解决方案里有两个同名类一个在WinForms项目里一个在类库里编译器选了错误的那一个导致类型不匹配。这种问题看错误信息里提示的命名空间就能发现。6. 把100例变成本地作品集三个进阶玩法6.1 组合改造三个例子拼成一个日志查看器单个例子无论多标准都算不上作品。我的做法是挑三个看似无关的例子做组合实验。比如文件读写正则提取DataGridView拼成一个日志查看器窗体左边放一个读取按钮中间是DataGridView表格正则负责从每行日志里提取时间、级别、消息三个字段。组合的价值在于强迫自己定义数据在模块间的流转方式正则提取出来的集合怎么塞进DataTableDataTable又怎么绑定到控件上这些衔接细节在单个例子里是学不到的。6.2 异常处理和日志落盘让代码从能跑到抗造例子集的代码普遍缺少异常处理直接把输入框里的值拿去解析用户一旦输入不可转换的内容整个程序就会崩溃。进阶版本我统一加一个try-catch结构catch块里调用自己写的LogHelper.WriteError方法把异常信息和当前时间追加写入一个error.log文本文件。这个LogHelper本身又是对文件读写例子的复用。至此例子集里的零散知识开始形成一个闭合循环写日志用的是文件例子解析输入用的是正则例子展示结果用的是控件例子每个环节其实都是前面练过的。6.3 把算法抽到独立类库用单元测试做回归保险挑排序、二分查找这类逻辑纯函数的例子把它们从窗体代码中抠出来放进一个单独的类库项目再建一个单元测试项目引入它。写三个测试方法分别覆盖正常值、边界值空数组、单个元素、重复元素。运行测试通过后这些方法就可以放心复用了。从那以后我每次拿到新的实例集都会强制走一遍先分类→按顺序刷→最后挑几个例子组合改造并加测试的流程希望这个习惯也能帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?