ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

C#实战练习题:从字符串处理到单例模式,夯实上位机开发基础

C#实战练习题:从字符串处理到单例模式,夯实上位机开发基础 简介面向C#初学者与希望巩固语法基础的开发者这套习题资源用大量可运行示例覆盖从基本数据类型到泛型、LINQ、异步编程等核心知识点既能配合教材逐章练习也可作为面试前快速回顾的题库。压缩包共1670个文件以224个C#源码文件为主体另有164个exe可执行程序、172个pdb调试文件及多个资源文件与项目工程文件整体约17.87MB解压后即可运行核对。资源已有725人学习范围涵盖基本语法、控制结构、函数与数组、类与对象、接口与抽象类、异常处理、委托与事件、文件流、LINQ查询及async/await异步方法。通过修改和调试这些实例代码能深入理解值传递与引用传递、封装继承多态、集合操作、文件读写等易混淆概念形成面向对象编程思维。从近百个示例工程来看这套材料以教学实例为导向适合零基础者在短时间内完成C#入门训练。 很多人在学C#的时候都有一种错觉看视频、抄代码都挺顺一关掉教程自己写马上卡壳。尤其那些冲着“C#上位机开发”“机器视觉”来的朋友往往一上来就想去碰Socket、串口、SDK结果连字符串处理都写不利索最后被各种小报错磨掉耐心。市面上“C#基础练习题”这类资料其实不少但大多停留在“求1到100的和”“判断闰年”这种为做题而做题的层次跟真实开发场景脱节。我这篇不打算再给你列一堆教科书题而是把练习按“实际项目里一定会用到”的标准重新组织一遍——每道题背后都对应一个具体需求场景。你如果能把这套题刷明白再去碰上位机、Web API、Halcon联合编程这些东西底气会完全不一样。1. 开始之前环境配置里最容易绊倒新手的三个坑既然要做练习第一个要解决的是“代码在哪跑”的问题。很多新手上来就装了个Visual Studio然后照着老教程写代码结果控制台程序跑起来黑窗口一闪而过或者压根连编译都过不了。这里说三个我见过的、出现频率极高的环境坑。1.1 版本选不对教材里的代码就是跑不起来现在微软官方主推的版本是.NET 6/7/8如果你去微软官网下载SDK默认装的就是这个系列。但很多早期教程、老面试题书里用的还是.NET Framework 4.x或者.NET Core 3.1。这两套东西新建项目的模板结构差别挺大尤其是新版控制台模板默认不生成Program.cs里的Main方法而是用顶级语句直接导致你抄老代码时一头雾水。我给你的建议是如果是纯学习基础语法直接装最新的.NET 8 SDK然后用Visual Studio Community免费的或VS Code加C# Dev Kit插件都行。我自己更推荐新手用VS Community因为调试器、断点、变量监视窗口这些功能开箱即用省得在配置上浪费精力。1.2 环境变量没配好命令怎么都执行不了用VS Code的同学经常会遇到dotnet --version在终端里能跑但重启电脑后就不认识了这基本是环境变量Path没配置好的问题。Windows下的.NET SDK安装包正常装完会自动把路径写进系统Path如果你从绿色版、便携版解压来的就得手动加一条指向SDK根目录的路径。验证环境是否OK我建议在终端里依次跑这三条命令dotnet --version dotnet --list-sdks dotnet new console -o HelloWorld cd HelloWorld dotnet run如果最后dotnet run能输出Hello, World!你的环境就彻底没问题了。这个步骤别急着跳过我见过太多人代码写对了结果卡在SDK缺失上白白浪费一晚上。2. 第一组练习变量、类型转换与控制流环境搞定之后先别急着去写花哨的功能把C#的语法地基打牢。这一组的题目看似基础但每一道我都加了“实际项目里你为什么需要它”的说明这样你才知道不是白练。2.1 练习题交换两个变量的值题目本身很朴素声明两个整型变量a 5, b 10写代码把它们的值互换。大多数人第一反应是引入第三个临时变量int a 5; int b 10; int temp a; a b; b temp; Console.WriteLine($a{a}, b{b});这当然对但我想让你同时知道C#里的更优雅写法——元组解构int a 5; int b 10; (a, b) (b, a); Console.WriteLine($a{a}, b{b});这道题练的是什么是“你在初级开发里最常见的一个动作变量赋值”。很多初学者搞不清“赋值是拷贝值还是拷贝引用”理解透这个基准题后面学类、学对象、学参数传递时就不会懵。另外提一嘴面试官如果问你“怎么在不使用第三个变量的情况下交换两个整数”那是在考异或运算a ^ b; b ^ a; a ^ b;知道有这么回事就行实际项目里没人这么写。2.2 练习题把字符串转成数字别再用Parse裸奔实际开发中从文本框、配置文件、Excel、扫码枪拿到的数据几乎全是字符串但你计算时又需要数字。比如上位机里从扫码枪读到的可能是一串数字字符串你要把它转成int才能做校验。C#里最常见的三种转换方式string text 123; // 方式一Convert int num1 Convert.ToInt32(text); // 方式二Parse int num2 int.Parse(text); // 方式三TryParse bool success int.TryParse(text, out int num3);我强烈建议你在练习时就养成用TryParse的习惯而不是前两种。原因很简单Convert和Parse在遇到abc、、null时会抛异常程序直接崩而TryParse返回bool告诉你是否成功不抛异常顶多是你自己决定失败后怎么处理。这一点做上位机的人体会最深——设备传来的数据偶尔就是脏的你如果不在解析阶段兜底后面写再多功能都是白搭。练习时可以自己写一段代码分别传合法数字、空字符串、带字母的字符串进去观察三种方式的行为差异。这道题的背后是对“程序鲁棒性”的第一次感知。2.3 练习题用循环打印九九乘法表只算入门你在网上能搜到各种打印九九乘法表的写法外层循环i从1到9内层循环j从1到i拼字符串输出。这题本身不难但你练完之后要能回答自己三个问题为什么内层循环的结束条件得是j i而不是j 9为什么用Console.Write而不是Console.WriteLine怎么控制对齐让输出像一个三角形而不是歪歪扭扭。更进阶一点的变体是把乘法表改成倒序输出、接龙式输出、用StringBuilder拼接成一段字符串再一次性输出。最后这个StringBuilder的点很关键——你将来写日志、拼报文、拼上位机协议帧时都会用到它比反复string 拼接性能好得多也避免产生一堆无用的中间字符串对象。3. 第二组练习字符串、日期与集合——频率最高的三个操作区刷完第一组你已经有了基本的逻辑能力。接下来这组更贴近日常开发字符串、日期、集合。搜索热词里“C#语言怎样截取字符串”出现频率很高说明大家都在实际场景里卡住了。这一组题目练完你的“抄作业”面会宽很多。3.1 拆字符串不只是Split那么简单实际场景举例你从配置文件里读了一行张三,25,开发部想拆出姓名、年龄、部门。用Splitstring line 张三,25,开发部; string[] parts line.Split(,); string name parts[0]; int age int.Parse(parts[1]); string department parts[2];但真实数据往往没这么乖比如可能带空格、带引号、有多余逗号。这时候就要学会组合拳string line 张三, 25 , 开发部 ; string[] parts line.Split(,); string name parts[0].Trim(); int age int.Parse(parts[1].Trim()); string department parts[2].Trim();Trim去掉首尾空白这个细节工程里极其常用。更进一步如果你要截取一段字符串中的指定部分比如订单号:SO-20240612-001里的SO-20240612-001就得配合IndexOf定位冒号的位置再用Substring截取。练习时建议自己封装一个方法static string ExtractAfter(string source, string delimiter) { int index source.IndexOf(delimiter); if (index 0) return string.Empty; return source.Substring(index delimiter.Length).Trim(); }注意检查IndexOf返回值为-1表示没找到这时候直接截取就会崩。这种“找不到就返回空串”的防御式写法反映的是工程意识而不是单纯语法熟练度。3.2 日期时间处理格式化与计算差值搜索词里“C#更新本地时间”其实指的是利用DateTime.Now获取当前时间或者同步系统时间。日期时间这块新手最容易搞混的是格式化字符串。先记住几个最常用的DateTime now DateTime.Now; Console.WriteLine(now.ToString(yyyy-MM-dd HH:mm:ss)); // 2025-01-08 14:30:00 Console.WriteLine(now.ToString(yyyyMMddHHmmss)); // 20250108143000 Console.WriteLine(now.ToString(yyyy年MM月dd日 dddd)); // 2025年01月08日 星期三yyyy是四位年份MM是两位月份HH是24小时制hh是12小时制mm是分钟ss是秒。初学者最容易把月份和分钟都写成mm导致输出完全不是自己想要的。再练一个高频操作计算两个日期之间相差多少天。比如计算设备上次保养距今几天DateTime lastMaintenance new DateTime(2024, 12, 1); DateTime now DateTime.Now; TimeSpan diff now - lastMaintenance; Console.WriteLine($已保养 {diff.TotalDays:F1} 天);TimeSpan就是两个时间点之间的差TotalDays属性直接告诉你天数。这些属于“你可以靠查资料查到但你自己不写一遍用的时候还是会卡”的知识点。3.3 集合实战List和Dictionary集合是C#里最常打交道的类型家族。热词里有“泛型”相关的提问说明很多人传参时被ListT、DictionaryK,V搞晕过。练习场景统计一段英文文章里每个单词出现的次数。这是Dictionary的经典用法。string text hello world hello csharp world world; string[] words text.Split( ); Dictionarystring, int counts new Dictionarystring, int(); foreach (string word in words) { if (counts.ContainsKey(word)) counts[word]; else counts[word] 1; } foreach (var pair in counts) Console.WriteLine(${pair.Key}: {pair.Value});这里的关键理解点Dictionary的键是唯一的你不能往里面塞两个同样的键所以写入前要判断是否存在。等你把这段写顺了后面处理配置表、设备参数映射、JSON反序列化结果时会非常顺手。ListT的练习可以这样设计做一个学生分数列表求最高分、最低分、平均分、按分数排序、去重。先试着自己用循环实现再去学Linq里的Max()、Min()、Average()、OrderByDescending()你才会真正理解这些扩展方法帮我们省了多少事。4. 第三组练习用类和继承组织代码顺便把单例看了基础语法刷得差不多了就该上面向对象。很多新手学OOP时觉得“抽象”“封装”这些词太虚其实就是因为没在具体场景里用过。这一组我用一个贴近业务的例子串起来。4.1 练习题设计一个员工类先别管什么高大上的设计模式从最简单的开始定义一个Employee类包含工号、姓名、工资提供构造函数、属性和一个计算年薪的方法。public class Employee { public string Id { get; set; } public string Name { get; set; } public decimal MonthlySalary { get; set; } public Employee(string id, string name, decimal monthlySalary) { Id id; Name name; MonthlySalary monthlySalary; } public decimal GetAnnualSalary() { return MonthlySalary * 12; } }写完之后问自己三个问题为什么字段要用属性{ get; set; }而不是public字段因为在属性里你可以加校验逻辑比如工资不能为负数将来改内部实现不影响外部调用。构造函数的作用是什么它保证了对象创建时就处于一个合法状态。热词里有人搜“C#单例模式”单例的私有构造函数其实就是这个思路的延伸——不让外部随便new由类自己控制实例数量。如果有一个Manager类它应该继承Employee还是组合一个Employee这里不急着给标准答案但你要开始思考“is-a”和“has-a”的区别。继承在C#里是单继承一个类只能有一个父类所以如果用继承Manager可以额外加Bonus属性和重写后的GetAnnualSalary。4.2 练习题手写一个线程安全的单例“单例模式”是面试题和实际项目中的常客。最简单的写法是懒汉式加锁我建议你把下面这个版本背熟并且理解它每一行的作用public class ConfigManager { private static readonly object _lock new object(); private static ConfigManager _instance; private ConfigManager() { } public static ConfigManager Instance { get { if (_instance null) { lock (_lock) { if (_instance null) { _instance new ConfigManager(); } } } return _instance; } } }这串代码你面试时能默写出来基本就过关了。但比默写更重要的是理解为什么加锁还要再判一次空因为两个线程可能同时通过了第一个if如果不在锁内再查一次就会new出两个实例。这个模式叫“双重检查锁定”你在上位机里管理串口对象、日志对象、配置对象时都会用到。4.3 练习题struct和class到底该选谁热词里有人搜“C#结构体”这确实是个容易混的点。简单记忆法class是引用类型存在堆上传递时传引用两个变量可能指向同一个对象struct是值类型存在栈上或作为字段内嵌传递时完整拷贝一份。什么时候用struct表示一个小的、不可变的数据载体时比如坐标点Point、颜色Color、一个协议报文头。什么时候别用对象较大、需要继承多态、生命周期较长时。这道题不用写多复杂试着定义一个坐标struct再定义一个坐标class分别放进List里修改其中一个元素的X值你会看到行为完全不同——List里存struct时修改的是副本存class时修改的是原对象。这个现象实测一遍比背十遍理论都管用。5. 第四组练习文件读写和异常处理让程序真正能交付前面的代码都在内存里玩程序一关数据就没了。但真正的业务系统文件读写、日志记录是绕不过去的。热词里有“C#后台处理前端传过来的excel”“asp.net c# streamreader”等搜索说明很多人入职后第一件事就是跟文件打交道。5.1 练习题读取CSV文件并计算平均值CSV是最常见的文本数据格式结构简单每行用逗号分隔。练习目标读取一个包含多行成绩的文本文件每行是“姓名,分数”统计所有人的平均分并输出。string path C:\temp\scores.csv; string[] lines File.ReadAllLines(path, Encoding.UTF8); int total 0; int count 0; foreach (string line in lines) { if (string.IsNullOrWhiteSpace(line)) continue; string[] parts line.Split(,); if (parts.Length 2) continue; if (int.TryParse(parts[1].Trim(), out int score)) { total score; count; } } double average count 0 ? 0 : (double)total / count; Console.WriteLine($平均分: {average:F2});这里有两个新手必踩的坑。第一个坑是编码用Windows记事本另存为UTF-8时可能带BOM用File.ReadAllLines默认也会处理但如果你从某些老系统导出的是GB2312编码文件读出来就是乱码。这时候要指定Encoding.GetEncoding(GB2312)或使用Encoding.Default具体得看来源系统。第二个坑是脏数据文件里可能有空行、格式不完整的行、转不了数字的字符串。我上面的代码用TryParse加continue跳过异常行这就是工业级代码的思路——数据不干净是常态程序不能一遇到脏数据就崩。5.2 练习题写一个带日志记录的异常处理模块实际开发中程序抛异常不可怕可怕的是异常信息没记录下来出问题了不知道哪里崩的。热词里有人搜“C#方法”本质就是想学会把逻辑封装成可复用的方法。练习要求写一个方法负责把一个文件路径作为参数传入读取文件内容如果文件不存在就记录错误日志并返回空字符串。static string ReadFileSafely(string path) { try { if (!File.Exists(path)) { Log($[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] 文件不存在: {path}); return string.Empty; } return File.ReadAllText(path, Encoding.UTF8); } catch (UnauthorizedAccessException) { Log($[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] 无权限访问: {path}); return string.Empty; } catch (Exception ex) { Log($[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] 读取失败: {ex.Message}); return string.Empty; } } static void Log(string message) { File.AppendAllText(C:\temp\app.log, message Environment.NewLine, Encoding.UTF8); }try-catch的关键点在于能捕获具体异常就尽量捕获具体异常最后一个catch (Exception ex)兜底。别用空的catch {}把异常吞掉那样出了问题连日志都没有排查起来等于大海捞针。File.AppendAllText这个API很实用反复调用也不会覆盖之前的日志。6. 综合练习把上述内容串成一个成绩管理系统前面练了那么多题最后一步一定要把它们串起来。单独的知识点就像零散的积木你可千万别只会搭个单块得能拼成一个完整的小房子。这道综合题也是网上常见的“简单OA系统”或“学生管理系统”类题目的小型化版本题目做一个控制台成绩管理程序支持以下功能启动时从scores.txt文件读取学生成绩显示菜单查看所有学生成绩、添加学生成绩、删除学生成绩、保存并退出查看时按总分排序添加的学生记录要能写回文件整个程序不能因为用户输入错误而崩溃。这个练习看着简单但你实际动手写会发现它同时用到了类与对象学生类、集合List存储、字符串处理解析每行文本、文件读写加载和保存、异常处理文件不存在、输入不合法、甚至一点设计思维把显示菜单和执行逻辑分开。我的建议是先自己写一版不用管写得好不好能跑就行。然后回头再审代码问自己几个问题菜单循环写对了吗退出时是break还是return添加学生时成绩输入用int.Parse还是int.TryParse文件保存时是用File.WriteAllLines一次写入还是每添加一条就AppendAllText一次删除学生时是按姓名匹配还是按序号匹配删不到人时程序会怎样这四道追问比题本身更有价值。如果你能把这四句话对应的代码都改得漂亮说明你对C#基础的理解已经进入“能干活”的层面了。最后再分享一个我自己的习惯学编程别只“看题解题”最好给自己准备一个小本子或者电子笔记每做错一道题、每踩一个坑把报错信息、原因、解决办法记下来。C#的很多坑是有共性的比如“序列化时循环引用”“DateTime默认值不是null而是0001年1月1日”“字符串比较用还是Equals”这些东西光靠脑子记不靠谱写下来才能变成自己的武器库。等你刷完这套基础题试着去找一个简单的上位机项目或者Web API接口练练手那时你会发现自己比之前从容得多。本文还有配套的精品资源点击获取
返回列表