C# 快速入门
14 天从零到能读写中等规模 C# 程序。面向有编程基础的同学,主打"快速入门",覆盖到大二核心内容:类型、流程、方法、OOP、泛型、LINQ、异常与文件。
初识 C# 与 .NET
从 Hello World 开始,理解 C# 的编译与运行
C# 是一门强类型、面向对象的现代语言,运行在 .NET 平台之上。理解它的执行链很重要:
源码 (.cs)→ C# 编译器 →IL(中间语言,程序集 .dll/.exe)IL→ CLR 的 JIT 即时编译 → 机器码执行- CLR(公共语言运行时)负责内存管理(GC)、类型安全、异常等
经典程序结构:namespace → class → Main 方法(入口点)。C# 9+ 支持顶层语句,可省略 Main 与 class,文件级代码直接执行——非常适合入门。
using System;
namespace HelloApp
{
class Program
{
static void Main()
{
Console.WriteLine("Hello, C#!");
int a = 10, b = 20;
Console.WriteLine($"a + b = {a + b}");
}
}
}
// C# 9+ 顶层语句:省略 Main 与 class
using System;
Console.WriteLine("Hello, C#!");
int a = 10, b = 20;
Console.WriteLine($"a + b = {a + b}");
a + b = 30
using System; 引入命名空间;Console.WriteLine 输出一行;$"..." 是字符串插值,{表达式} 会被替换为值。 Q1C# 源代码经编译器编译后的中间产物是什么?
- A 机器码
- B IL(中间语言)
- C Java 字节码
- D 汇编代码
Q2控制台程序的入口方法是?
- A Start
- B Run
- C Main
- D Init
补全代码:让程序输出 Hello, C#!
class Program
{
static void Main()
{
Console.▓▓▓("Hello, C#!");
}
}
Console.WriteLine(...)。变量与数据类型
值类型 / 引用类型、常用类型与安全转换
C# 类型分两大类:值类型(int、double、bool、char、struct 等,分配在栈上,赋值是复制)和 引用类型(string、class、数组等,引用指向堆上对象)。
- 整数:
intlongshortbyte - 浮点:
float(F)doubledecimal(M,高精度,适合金额) - 其它:
boolcharstring var让编译器推断类型;const声明常量
类型转换:隐式(安全、无损)→ 显式 cast → Convert/Parse → TryParse(不抛异常,最安全)。
int age = 20;
double pi = 3.14159;
decimal price = 19.99m; // 财务计算用 decimal
bool isStudent = true;
char grade = 'A';
string name = "Gavin";
var city = "Shenzhen"; // var 推断为 string
const double Gravity = 9.8;
// 类型转换
double d = 9.7;
int n = (int)d; // 显式转换,截断 -> 9
string s = "123";
int parsed = int.Parse(s); // 123
if (int.TryParse("abc", out int result))
Console.WriteLine(result);
else
Console.WriteLine("解析失败");
123
解析失败
decimal 后缀 m、float 后缀 f。金额计算永远用 decimal,浮点有精度误差。 Q1适合财务/货币计算的高精度类型是?
- A float
- B double
- C decimal
- D int
Q2int.TryParse 转换失败时会怎样?
- A 抛出异常
- B 返回 false
- C 返回 null
- D 程序崩溃
用安全的方式把字符串 "42" 解析成整数,成功才输出。
string s = "42";
if (int.▓▓▓(s, out int n))
Console.WriteLine(n);
int.TryParse(s, out int n) 不抛异常,返回 bool。运算符与表达式
算术 / 逻辑 / null 运算符与字符串插值
- 算术:
+ - * / %。注意整数除法7/2 == 3,要小数写7/2.0 - 逻辑:
&& || !,支持短路求值 - null 相关:
??空合并(左为 null 取右)、?.空条件(左为 null 不调用) - 三元:
条件 ? 真 : 假 - 字符串插值:
$"你好,{name}!"
int a = 7, b = 2;
Console.WriteLine(a / b); // 3(整数除法)
Console.WriteLine(a % b); // 1
Console.WriteLine(a / 2.0); // 3.5
bool adult = a > 5 && b < 5; // 短路求值
string name = null;
string display = name ?? "匿名"; // "匿名"
int? len = name?.Length; // null,不抛异常
string city = "深圳";
Console.WriteLine($"欢迎,{city}!"); // 字符串插值
int max = a > b ? a : b; // 三元
1
3.5
匿名
欢迎,深圳!
Q1<code>a && b</code> 中 a 为 false 时,b 是否被求值?
- A 会求值
- B 不求值(短路)
Q2<code>name ?? "匿名"</code> 的含义是?
- A name 为 null 时用 "匿名"
- B name 等于 "匿名"
- C 抛出异常
- D 字符串拼接
当 name 为 null 时,让 display 取默认值 "匿名"。
string name = null;
string display = name ▓▓▓ "匿名";
??。控制流
条件、switch 表达式与循环
C# 控制流与多数语言相似,但有现代化增强:
if / else if / else条件分支- switch 表达式(C# 8+):用
=>映射,支持关系模式与_通配 - 循环:
whiledo-whileforforeach break跳出、continue跳过本次
int score = 85;
// switch 表达式 + 关系模式
string grade = score switch
{
>= 90 => "A",
>= 80 => "B",
>= 60 => "C",
_ => "D"
};
Console.WriteLine($"等级:{grade}");
// for 求和 1..100
int sum = 0;
for (int i = 1; i <= 100; i++)
sum += i;
Console.WriteLine($"sum = {sum}"); // 5050
// foreach 遍历字符串
foreach (var ch in "C#")
Console.Write(ch + " ");
sum = 5050
C #
Q1遍历数组/集合最方便的循环是?
- A for
- B while
- C foreach
- D do-while
Q2switch 表达式中 <code>_</code> 表示?
- A 乘法
- B 默认/通配
- C 指针
- D 空格
补全 for 循环,让 i 每次自增 1,求 1 到 100 的和。
int sum = 0;
for (int i = 1; i <= 100; ▓▓▓)
sum += i;
i++ 或 i += 1。方法
参数传递、ref/out、重载与递归
- 方法 = 一段可复用代码,有签名(名称 + 参数列表)和返回类型
- 值传递(默认):复制实参;ref:传入引用,可修改原变量;out:输出参数,方法内必须赋值
params可变参数;方法重载:同名不同参数列表- 递归:方法调用自身,必须有终止条件
// 递归阶乘
static long Factorial(int n)
{
if (n <= 1) return 1;
return n * Factorial(n - 1);
}
// out 返回多个值
static void Divide(int a, int b, out int quotient, out int remainder)
{
quotient = a / b;
remainder = a % b;
}
// ref 修改实参
static void Increment(ref int x) => x++;
// params 可变参数
static int Sum(params int[] nums)
{
int total = 0;
foreach (var n in nums) total += n;
return total;
}
Console.WriteLine(Factorial(5)); // 120
Divide(17, 5, out int q, out int r);
Console.WriteLine($"{q} ... {r}"); // 3 ... 2
int v = 10; Increment(ref v);
Console.WriteLine(v); // 11
Console.WriteLine(Sum(1, 2, 3, 4)); // 10
3 ... 2
11
10
Q1关于 ref 和 out,正确的是?
- A 都不需赋初值
- B out 参数在方法内必须赋值
- C ref 不需赋初值
- D 两者完全相同
Q2方法重载的依据是?
- A 返回类型
- B 参数列表
- C 方法名不同
- D 访问修饰符
补全调用:用 out 接收 Divide 返回的两个结果。
Divide(17, 5, ▓▓▓ int q, out int r);
out 关键字修饰。数组与字符串
数组操作、字符串不可变性与 StringBuilder
- 一维数组:
int[] a = {1,2,3};;Array.Sort/IndexOf/Reverse等工具方法 - 多维:
int[,] m矩形;int[][] j锯齿数组(数组的数组) - string 不可变:每次拼接都生成新对象。
Length / Substring / IndexOf / Split / Replace / Contains / Trim - 高频拼接用
StringBuilder,避免大量临时字符串
int[] nums = { 3, 1, 4, 1, 5, 9, 2, 6 };
Array.Sort(nums);
Console.WriteLine(string.Join(", ", nums)); // 1, 1, 2, 3, 4, 5, 6, 9
// 二维数组
int[,] matrix = { { 1, 2 }, { 3, 4 } };
// 字符串常用方法
string s = "Hello, World";
Console.WriteLine(s.Length); // 12
Console.WriteLine(s.Substring(0, 5)); // Hello
Console.WriteLine(s.IndexOf(',')); // 5
string[] parts = "a,b,c".Split(',');
Console.WriteLine(string.Join("|", parts)); // a|b|c
// StringBuilder 高频拼接
var sb = new System.Text.StringBuilder();
for (int i = 0; i < 3; i++)
sb.Append(i).Append(" ");
Console.WriteLine(sb.ToString()); // 0 1 2
12
Hello
5
a|b|c
0 1 2
Q1C# 中 string 对象是?
- A 可变的
- B 不可变的
- C 值类型
- D 没有长度
Q2大量字符串拼接应优先用?
- A + 拼接
- B StringBuilder
- C char 数组
- D 固定 string
把字符串 "a,b,c" 按逗号拆分成数组。
string[] parts = "a,b,c".▓▓▓(',');
Split。类与对象
OOP 起步:字段、属性、方法、构造函数
类是蓝图(模板),对象是用 new 创建的实例。类封装数据(字段/属性)与行为(方法)。
- 字段:私有存储数据;属性:对外暴露的受控访问(get/set)
- 构造函数:与类同名,初始化对象
- 访问修饰符:
public公开、private私有(默认)、protected子类可访问、internal程序集内 - static:属于类本身而非实例,所有实例共享
class Student
{
private int id; // 字段
public string Name { get; set; } // 自动属性
public int Id // 完整属性(带校验)
{
get => id;
set => id = value > 0 ? value : 0;
}
public Student(int id, string name) // 构造函数
{
Id = id;
Name = name;
}
public void Introduce() => Console.WriteLine($"我是 {Name},学号 {Id}");
public static int Count = 0; // 静态成员
}
var s = new Student(1, "小明");
s.Introduce(); // 我是 小明,学号 1
Student.Count++;
Q1关于类与对象,正确的是?
- A 类是对象的实例
- B 对象是类的实例
- C 二者完全相同
- D 类不能实例化
Q2static 成员属于?
- A 某个实例
- B 类本身(所有实例共享)
- C 命名空间
- D 某个方法
用关键字创建一个 Student 实例。
Student s = ▓▓▓ Student(1, "小明");
new 关键字。属性、封装与构造函数进阶
自动属性、init、主构造函数与封装
- 自动属性:
public string Name { get; set; },编译器自动生成私有字段 - 只读属性:
{ get; }只能在构造函数中赋值 - init 设置器(C# 9+):仅对象初始化时可写,之后不可变,兼顾灵活与安全
- 完整属性:自定义 get/set 逻辑(如校验),配合私有字段
- 主构造函数(C# 12):参数直接写在类名后,整个类可见
class Account
{
public string Owner { get; set; } // 自动属性
public string Number { get; } // 只读属性(构造中赋值)
public decimal Balance { get; init; } // init:仅初始化时可写
public Account(string owner, string number)
{
Owner = owner;
Number = number;
}
private decimal rate; // 完整属性 + 校验
public decimal Rate
{
get => rate;
set => rate = value is >= 0 and <= 1 ? value
: throw new ArgumentException("利率非法");
}
}
// C# 12 主构造函数
class Point(double x, double y)
{
public double X => x;
public double Y => y;
public double Distance() => Math.Sqrt(x * x + y * y);
}
// 对象初始化器
var acc = new Account("张三", "A001") { Balance = 100m };
Q1init 设置器的作用是?
- A 任何时候可写
- B 仅对象初始化时可写
- C 只读不可写
- D 私有不可见
Q2自动属性 <code>public int X { get; set; }</code> 会?
- A 报错
- B 编译器自动生成私有字段
- C 没有字段
- D 只有 get
让 Balance 属性只能在初始化时赋值(之后只读)。
public decimal Balance { get; ▓▓▓; }
init。继承与多态
base、virtual/override、is/as
- 继承:
class Dog : Animal,复用基类成员;C# 单继承 - virtual / override:基类标记 virtual,子类用 override 重写,实现多态
- 多态:父类引用指向子类对象,调用方法时执行子类版本
base调用基类构造/方法;sealed禁止再被继承/重写is判断类型并声明变量;as安全转换(失败返回 null)
class Animal
{
public string Name { get; set; }
public Animal(string name) => Name = name;
public virtual void Speak() => Console.WriteLine("...");
}
class Dog : Animal
{
public Dog(string name) : base(name) { }
public override void Speak() => Console.WriteLine($"{Name}: 汪汪!");
}
class Cat : Animal
{
public Cat(string name) : base(name) { }
public override void Speak() => Console.WriteLine($"{Name}: 喵~");
}
// 多态:父类引用指向子类对象
Animal[] zoo = { new Dog("旺财"), new Cat("咪咪"), new Animal("未知") };
foreach (var a in zoo) a.Speak();
// is 模式匹配声明变量 / as 安全转换
object o = new Dog("小黑");
if (o is Dog d) d.Speak();
Animal x = o as Animal;
咪咪: 喵~
...
小黑: 汪汪!
Q1子类重写父类方法,父类方法需标记?
- A sealed
- B virtual(子类用 override)
- C static
- D private
Q2sealed 修饰类的作用是?
- A 不能被继承
- B 不能实例化
- C 抽象的
- D 公开的
在子类中重写父类的 Speak 方法。
class Dog : Animal
{
public ▓▓▓ void Speak() => Console.WriteLine("汪汪!");
}
override。抽象类与接口
abstract vs interface,何时用哪个
- 抽象类(abstract):不能实例化,可含字段、构造、普通方法、抽象方法;单继承
- 接口(interface):纯契约,定义方法签名;类可实现多个接口;C# 8+ 支持默认实现
- 选择:有共享实现/字段 → 抽象类;定义跨类型的能力契约 → 接口
- 接口命名常以
I开头,如IComparableIEnumerable
interface IShape
{
double Area();
void Print() => Console.WriteLine($"面积 = {Area():F2}"); // 默认方法
}
abstract class Shape
{
public abstract double Area(); // 抽象方法,子类必须实现
}
class Circle : Shape, IShape
{
public double Radius { get; set; }
public Circle(double r) => Radius = r;
public override double Area() => Math.PI * Radius * Radius;
}
class Rect : Shape, IShape
{
public double W { get; set; }
public double H { get; set; }
public override double Area() => W * H;
}
Shape s1 = new Circle(2);
IShape s2 = new Rect { W = 3, H = 4 };
Console.WriteLine(s1.Area()); // 12.566...
s2.Print(); // 面积 = 12.00
面积 = 12.00
Q1一个类可以实现几个接口?
- A 仅 1 个
- B 任意多个
- C 2 个
- D 0 个
Q2abstract 类能否用 new 直接实例化?
- A 能
- B 不能
让 Circle 同时继承 Shape 并实现 IShape 接口。
class Circle : Shape, ▓▓▓
{
public override double Area() => ...;
}
异常处理
try/catch/finally 与自定义异常
try放可能出错的代码;catch捕获特定异常;finally无论是否异常都执行(清理资源)- 异常是类,继承自
Exception;常见:NullReferenceExceptionIndexOutOfRangeExceptionFormatExceptionDivideByZeroException throw抛出;catch (...) when (条件)异常筛选器- 自定义异常继承
Exception,命名以 Exception 结尾
try
{
int[] arr = { 1, 2, 3 };
Console.WriteLine(arr[5]); // 越界
}
catch (IndexOutOfRangeException ex)
{
Console.WriteLine("越界:" + ex.Message);
}
catch (DivideByZeroException)
{
Console.WriteLine("不能除以零");
}
catch (Exception ex) when (ex is FormatException)
{
Console.WriteLine("输入格式错误");
}
finally
{
Console.WriteLine("清理完成");
}
// 自定义异常
class InvalidScoreException : Exception
{
public InvalidScoreException(string msg) : base(msg) { }
}
static void CheckScore(int s)
{
if (s < 0 || s > 100)
throw new InvalidScoreException($"非法分数:{s}");
}
Q1finally 块何时执行?
- A 仅异常时
- B 仅正常时
- C 无论是否异常都执行
- D 从不执行
Q2自定义异常一般继承自?
- A Exception(或其子类)
- B Object
- C Array
- D string
补全代码,捕获 try 块中抛出的异常。
try
{
throw new Exception("出错了");
}
▓▓▓ (Exception ex)
{
Console.WriteLine(ex.Message);
}
catch 关键字。泛型与集合
List、Dictionary、泛型方法与约束
泛型让你写一份代码适用于多种类型,且类型安全、避免装箱。常用集合都在 System.Collections.Generic:
List<T>:可变长度列表,Add/Remove/Contains/CountDictionary<TKey, TValue>:键值对,键唯一,TryGetValueHashSet<T>:去重集合- 泛型约束:
where T : IComparable<T>限制 T 必须可比较
using System.Collections.Generic;
// 泛型 List<T>
var nums = new List<int> { 3, 1, 4, 1, 5 };
nums.Add(9);
nums.Remove(1); // 移除第一个 1
Console.WriteLine(nums.Count); // 5
Console.WriteLine(nums.Contains(4)); // True
// 泛型 Dictionary
var dict = new Dictionary<string, int>();
dict["apple"] = 3;
dict["banana"] = 5;
if (dict.TryGetValue("apple", out int cnt))
Console.WriteLine(cnt); // 3
// 泛型方法 + 约束
static T Max<T>(T a, T b) where T : IComparable<T>
=> a.CompareTo(b) >= 0 ? a : b;
Console.WriteLine(Max(3, 7)); // 7
Console.WriteLine(Max("cat", "dog")); // dog
True
3
7
dog
Q1泛型的主要好处是?
- A 性能更低
- B 类型安全 + 避免装箱
- C 只能存 int
- D 不可复用
Q2Dictionary<K,V> 中键的要求是?
- A 必须是 int
- B 必须唯一
- C 可以重复
- D 必须是 string
为泛型方法 Max 添加约束:T 必须实现 IComparable<T>。
static T Max<T>(T a, T b) ▓▓▓ T : IComparable<T>
=> a.CompareTo(b) >= 0 ? a : b;
where 关键字。LINQ 基础
查询表达式、方法链与延迟执行
LINQ(Language Integrated Query)让你用统一语法查询任何可枚举数据源,是 C# 的杀手锏特性。
- 两种写法:方法语法(链式调用,更常用)与查询语法(类 SQL)
- 常用:
Where筛选、Select投影、OrderBy排序、GroupBy分组、First/Any/Sum/Average聚合 - 延迟执行:Where/Select/OrderBy 等直到遍历时才真正计算;
ToList()立即执行 - 多用 lambda:
s => s.Score >= 80
using System;
using System.Collections.Generic;
using System.Linq;
record Student(int Id, string Name, int Score);
var students = new List<Student>
{
new(1, "Alice", 88),
new(2, "Bob", 72),
new(3, "Carol", 95),
new(4, "Dave", 60),
};
// 方法语法
var top = students
.Where(s => s.Score >= 80)
.OrderByDescending(s => s.Score)
.Select(s => new { s.Name, s.Score });
foreach (var s in top)
Console.WriteLine($"{s.Name}: {s.Score}");
// Carol: 95 Alice: 88
// 查询语法(等价)
var q = from s in students
where s.Score >= 80
orderby s.Score descending
select new { s.Name, s.Score };
// 聚合
Console.WriteLine(students.Average(s => s.Score)); // 78.75
Console.WriteLine(students.Any(s => s.Score == 100)); // False
Alice: 88
78.75
False
Q1LINQ 查询运算符多为?
- A 立即执行
- B 延迟执行(枚举时)
- C 编译期执行
- D 不执行
Q2Select 的作用是?
- A 筛选
- B 投影/转换每个元素
- C 排序
- D 分组
用 LINQ 筛选出分数 ≥ 80 的学生。
var top = students.▓▓▓(s => s.Score >= 80);
Where。委托、事件与文件 I/O
Action/Func、事件机制与文件读写(综合收尾)
- 委托(delegate):方法的"类型化指针"。
Action<T>无返回值、Func<T,TResult>有返回值,配合 lambda 极常用 - 事件(event):基于委托的发布-订阅机制,类外只能
+=/-=订阅,不能直接触发 - 文件 I/O:
System.IO下File/StreamReader/StreamWriter读写文本
using System;
using System.IO;
// 委托、Action、Func
Func<int, int, int> add = (a, b) => a + b;
Console.WriteLine(add(3, 4)); // 7
Action<string> log = msg => Console.WriteLine($"[LOG] {msg}");
log("启动");
// 事件:发布-订阅
class Heater
{
public event EventHandler Boiled;
public void Boil()
{
Console.WriteLine("加热中...");
Boiled?.Invoke(this, EventArgs.Empty);
}
}
class Alarm
{
public void Ring(object sender, EventArgs e)
=> Console.WriteLine("滴滴!水开了");
}
var h = new Heater();
var a = new Alarm();
h.Boiled += a.Ring; // 订阅
h.Boil();
// 文件 I/O
File.WriteAllText("note.txt", "Hello C#");
string text = File.ReadAllText("note.txt");
Console.WriteLine(text);
[LOG] 启动
加热中...
滴滴!水开了
Hello C#
Q1Action 与 Func 的区别是?
- A 无区别
- B Func 有返回值,Action 无
- C Action 有返回值
- D 都无参数
Q2event 相比普通委托的限制是?
- A 无区别
- B 类外只能 += / -= 订阅,不能直接调用
- C 不能订阅
- D 只读
让报警器 a.Ring 订阅热水器 h.Boiled 事件。
h.Boiled ▓▓▓ a.Ring;
+= 运算符。🎓 结业测验
综合检验 14 天所学,共 6 题
恭喜走到这里!完成下面 6 道综合题,验证你的 C# 快速入门成果。全部答对即可获得结业认证。
G1C# 程序的执行流程是?
- A 源码 → 机器码 → 运行
- B 源码 → IL → JIT → 机器码
- C 源码 → 解释执行
- D 源码 → 字节码 → 解释
G2下列关于 ref / out 说法正确的是?
- A 都需先赋初值
- B out 在方法内必须赋值
- C ref 不能修改原变量
- D out 用于输入
G3循环中拼接上千次字符串,最佳做法是?
- A 用 + 直接拼
- B 用 StringBuilder
- C 用 char[]
- D 每次 new string
G4实现运行时多态需要?
- A 子类用 new 隐藏方法
- B 父类 virtual + 子类 override
- C 都用 static
- D 用 sealed
G5关于接口,正确的是?
- A 一个类只能实现一个接口
- B 类可实现多个接口
- C 接口可含字段
- D 接口能实例化
G6LINQ 中 <code>students.Where(s => s.Score>80)</code> 何时真正过滤?
- A 调用 Where 时立即
- B 遍历结果时
- C 编译时
- D 从不