C# 进阶核心知识点汇总|多项目开发 + 委托 + Lambda + 事件一次吃透

刚入门C#后,光会写单项目控制台程序可不够,实际开发中不仅要会管理多项目、抽离公用代码,还得吃透委托、Lambda、扩展方法、事件这些核心语法------它们不仅是日常开发的高频考点,更是Linq、异步编程的基础,也是面试中常被问到的重点。

今天这篇文章,把C#进阶最核心的知识点一次性梳理清楚,从多项目开发的实战技巧,到索引器、密封类等基础进阶语法,再到委托-Lambda-事件的完整体系,每部分都配了可直接复制运行的代码示例,新手也能一步步吃透,建议收藏起来慢慢看、慢慢敲!

一、多项目开发:实战必备的项目管理技巧

实际开发中基本都是多项目协作,而非单项目,合理的项目结构能让代码更易维护、复用性更高。这部分重点讲项目创建、公用代码抽离、引用规则,都是落地即用的技巧。

1.1 项目创建的两种核心方式

Visual Studio中创建C#项目,核心有两种方式,按需选择即可:

  1. 项目和解决方案一起建立:新建项目时直接创建,适合简单的多项目场景,一步到位;
  2. 先建解决方案,再添加项目:先创建空解决方案,再右键「添加→新建项目」,适合复杂的多项目架构(比如业务项目、类库项目、测试项目分离),更灵活。

1.2 公用代码抽离:类库的创建与引用(核心)

多个项目需要共用的类(比如工具类、实体类),不要重复写 !抽离到类库项目(Class Library) 中,其他项目通过「添加引用」复用,这是解耦和提高复用性的关键,步骤如下:

  1. 添加类库:右键解决方案→添加→新建项目→选择「类库(.NET Framework/.NET Core)」,写入公用类;
  2. 添加引用:在需要使用公用类的项目上右键→添加→引用→勾选创建好的类库项目;
  3. 引用类方法 :在代码中通过using 类库命名空间;引入,之后直接使用类库中的类和方法即可。

1.3 多项目的配置文件读取规则

重要规则 :多项目中,只有主项目(当前运行的项目)app.config/web.config配置文件读取才会生效,其他项目的配置文件不会被加载。

如果类库项目需要读取配置,建议把配置项写在主项目的配置文件中。

1.4 第三方类库的引用与反编译

开发中用到第三方类库(比如NuGet包、dll文件),直接右键项目→添加→引用→勾选对应的dll即可;

如果需要查看第三方类库的具体代码,可通过反编译工具(比如ILSpy、dnSpy)查看,方便调试和理解用法。

二、基础语法进阶:索引器、密封类&静态类

这部分是C#的基础进阶语法,看似简单,实则是理解后续核心语法的铺垫,重点记本质和使用规则

2.1 索引器:无名称的"数组式属性"

索引器可以让我们像操作数组一样操作自定义类的对象,核心特点:

  1. 索引器没有名字 ,本质是this[参数] {get; set;}
  2. 不仅支持数字索引 ,还支持字符串索引,甚至允许多个索引器参数;
  3. 用法和属性类似,分为get(读取)和set(赋值),适合需要按"索引"访问对象内部数据的场景。

2.2 密封类&静态类:继承与实例化的限制

两者都是对类的继承/实例化做了限制,是面试高频考点,对比记忆更清晰:

类型 修饰符 核心规则
密封类 sealed 不能被继承,防止类的方法被子类重写,适合确定无需扩展的类
静态类 static 1. 不能被继承;2. 不能创建实例;3. 内部成员必须是static类型;4. 直接通过类名调用

补充 :static成员不仅能在静态类中定义,也能在非静态类 中定义,调用方式统一为类名.方法名/属性名,无需创建类的实例。

2.3 小补充:string类的扩展方法

string类是密封类(不能被继承),如果想给string扩展自定义方法,通过扩展方法实现(后文会详细讲),比如给string加一个"判断是否为数字"的方法,无需修改string原类。

三、C#核心:委托(Delegate)

委托(Delegate) 是C#的类型安全的函数指针 ,简单说:委托是用来封装方法 的类型,能把方法当作参数传递,是Lambda、事件、异步编程的基础。

核心记住:日常开发基本不用自定义委托,用.NET内置泛型委托就够了

3.1 自定义委托:基础语法

先了解自定义委托的写法,理解委托的本质,语法如下:

csharp 复制代码
// 定义委托:delegate + 返回值 + 委托名 + 参数列表
delegate 返回值 委托名(参数列表);
// 实例化委托:传入匹配的方法(方法的返回值、参数列表需和委托一致)
委托名 委托实例 = new 委托名(方法名);
// 简化写法:直接赋值方法名
委托名 委托实例 = 方法名;
// 调用委托
委托实例(参数);

代码示例

csharp 复制代码
namespace ConsoleApp12
{
    class Program
    {
        static void Main(string[] args)
        {
            // 实例化委托,两种写法都可
            MyDel myDel = new MyDel(GetName);
            // MyDel myDel = GetName;
            myDel("chen", 1);  // 调用委托,输出chen1
        }
        // 匹配委托的方法
        static string GetName(string name,int num)
        {
            return name + num;
        }
    }
    // 定义自定义委托
    delegate string MyDel(string name, int num);
}

3.2 实战优选:内置泛型委托Func & Action

.NET框架已经为我们封装了泛型委托Func和Action ,满足99%的开发场景,无需自定义委托,核心区别:

  1. Action :无返回值的委托,支持0~16个参数,比如ActionAction<string>Action<int, string>
  2. Func :有返回值的委托,支持0~16个参数,最后一个参数固定为返回值类型 ,比如Func<int>Func<string, int>Func<int, string, bool>

代码示例

csharp 复制代码
static void Main(string[] args)
{
    Action a1 = F1;        // 无参数无返回值
    Action<string> a2 = F2;// 1个参数无返回值
    Func<string,int> f1 = F3;// 1个参数,返回int
    a1(); // 调用F1
    a2("测试"); // 调用F2
    int res = f1("chen"); // 调用F3,获取返回值
}
static void F1() => Console.WriteLine("F1");
static void F2(string s1) => Console.WriteLine("F2:"+s1);
static int F3(string name) => 1;

3.3 匿名方法:无名称的临时方法

如果某个方法只需要用一次,没必要单独定义,用匿名方法 即可------本质是没有名字的方法 ,直接赋值给委托,语法:委托实例 = delegate(参数) { 方法体; }

csharp 复制代码
// 示例:匿名方法赋值给委托
Action<int> a = delegate (int s) { Console.WriteLine(s); };
a(10); // 输出10

注意:匿名方法是Lambda表达式的"前身",实际开发中已经被更简洁的Lambda替代,了解即可。

四、值传递进阶:ref 和 out

refout都是用来实现方法的按引用传递,解决C#中值类型参数默认按值传递、方法内修改不影响外部的问题,核心区别:

  1. ref :要求参数传入前必须初始化,方法内可读写;
  2. out :要求参数方法内必须赋值 ,传入前可未初始化,方法内写完后外部才能用。
    两者都是实战中优化参数传递的小技巧,比如方法需要返回多个值时,可结合out使用。

五、Lambda表达式:简化委托的"语法糖"(核心)

Lambda表达式是C#中最常用的语法糖 ,本质是匿名方法的简化写法 ,能极大减少代码量,也是Linq的核心语法,重点记简化规则,配练习食用效果更佳!

5.1 Lambda的核心简化规则

Lambda的核心符号是=>(读作"goes to"),结合委托使用,从匿名方法到Lambda的简化步骤是由繁到简,核心规则:

  1. 可省略参数的类型声明(编译器会自动推断);
  2. 只有一个参数时,可省略参数的圆括号()
  3. 方法体只有一行代码时,可省略大括号{}
  4. 有返回值且方法体只有一行时,可省略return关键字。

5.2 5个经典练习,吃透Lambda

这5个练习由浅入深,覆盖Lambda的所有使用场景,代码可直接复制到控制台运行,敲一遍就懂了!

练习1:基础简化(无返回值+有返回值)
csharp 复制代码
static void Main(string[] args)
{
    // 无返回值:一步步简化
    Action<int> a1 = delegate (int i)  { Console.WriteLine(i); };
    Action<int> a2 =  (int i)=>{ Console.WriteLine(i);};
    Action<int> a3 = i => { Console.WriteLine(i); };  // 单参数省略括号+类型
    a1(3333); a2(4444); a3(5555);

    // 有返回值:极致简化
    Func<int, string, bool> f1 = delegate (int i, string name) { return true; };
    Func<int, string, bool> f2 =  (int i, string name)=> {  return true; };
    Func<int, string, bool> f3 = (i, name) => { return true; }; // 省略类型
    Func<int, string, bool> f4 = (i, name) => true;  // 一行代码省略return+大括号
    f1(1, "chen"); f2(1, "chen"); f3(1, "chen"); f4(1, "chen");

    Console.ReadKey();
}
练习2:泛型结合Lambda,实现通用找最大值

自定义泛型方法,通过Lambda传递比较规则,让方法支持任意类型的数组找最大值,适配int、string、自定义实体等:

csharp 复制代码
static void Main(string[] args)
{
    int[] nums = new int[] { 3, 88, 6, 9 };
    // Lambda直接作为方法参数,传递比较规则
    int m = GetMax(nums, (i1, i2) => i1 > i2 );
    Console.WriteLine(m); // 输出88
    Console.ReadKey();
}
// 自定义比较方法(可被Lambda替代)
static bool compareInt(int i1,int i2) => i1 > i2;
// 泛型找最大值方法
static T GetMax<T>(T[] objs,Func<T,T,bool> compareFunc)
{
    T max = objs[0];
    for (int i = 0; i < objs.Length; i++)
    {
        if(compareFunc(objs[i],max)) max = objs[i];
    }
    return max;
}
练习3:自定义Where扩展方法(Linq底层原理)

实现Linq中Where的核心逻辑,通过扩展方法+Lambda实现集合过滤,理解Linq的底层本质:

csharp 复制代码
// 静态类:扩展方法的容器
static class JiHeExt
{
    // 扩展方法:this关键字+静态方法
    public static IEnumerable<T> MyWhere<T>(this IEnumerable<T> data,Func<T,bool> func)
    {
        List<T> resultList = new List<T>();
        foreach(T item in data)
        {
            if (func(item)) resultList.Add(item); // Lambda作为过滤规则
        }
        return resultList;
    }
}
class Program
{
    static void Main(string[] args)
    {
        int[] nums = new int[] { 3, 88, 6, 9 };
        IEnumerable<int> r1 = nums.MyWhere(i => i > 10); // 过滤大于10的数
        foreach (int item in r1) Console.WriteLine(item); // 输出88
        Console.ReadKey();
    }
}
练习4:Select方法:集合数据转换

Linq中Select的核心作用是数据处理/转换,通过Lambda对集合中每个元素做处理,生成新集合:

csharp 复制代码
static void Main(string[] args)
{
    List<int> list1 = new List<int> { 1, 2,3,8,16,99 };
    // Lambda将int转换为string,生成新集合
    IEnumerable<string> data = list1.Select(i => i +"你好");
    foreach (var item in data) Console.WriteLine(item); 
    // 输出:1你好、2你好、3你好、8你好、16你好、99你好
    Console.ReadLine();
}
练习5:Linq高频方法:Sum/ToList/ToArray

结合Lambda操作自定义实体集合,实战中高频使用,比如求和、集合类型转换:

csharp 复制代码
class Program
{
    static void Main(string[] args)
    {
        // 自定义实体数组
        Person[] p = new Person[]
        {
            new Person{Name="chen",Age=12},
            new Person{Name="chen2",Age=24},
            new Person{Name="chen3",Age=12}
        };
        int totalAge = p.Sum(i => i.Age); // 求和:12+24+12=48
        List<Person> psList = p.ToList(); // 转List集合
        Person[] psArray = p.ToArray();  // 转Array数组
        Console.WriteLine(totalAge); // 输出48
        Console.ReadKey();
    }
}
// 自定义实体类
class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

六、扩展方法:不修改原类,实现功能扩展

扩展方法是C#的黑科技 之一,核心作用是:在不修改原有类的代码、不继承原有类的前提下,为类添加新的方法 ,符合面向对象的开闭原则(对扩展开放,对修改关闭)。

扩展方法的三个必须条件(缺一不可)

  1. 扩展方法必须定义在静态类中;
  2. 扩展方法本身必须是静态方法
  3. 方法的第一个参数必须加this关键字,且this后紧跟要扩展的类(称为"扩展目标类")。

核心应用:Linq的所有方法(Where、Select、Sum等)都是通过扩展方法实现的,也是我们日常开发中给第三方类、系统类(如string、int)扩展方法的核心方式。

七、委托组合:多个委托的"批量执行"

C#支持委托的组合 ,通过+号将多个同类型的委托实例组合成一个委托链 ,调用组合后的委托时,会依次执行 委托链中的所有方法;也可通过-号从委托链中移除某个委托。
代码示例

csharp 复制代码
class Program
{
    static void Main(string[] args)
    {
        Mydel d1 = F1;
        Mydel d2 = F2;
        Mydel d3 = F3;
        Mydel d4 = d1 + d2 + d3; // 委托组合,形成委托链
        d4(8); // 依次执行F1、F2、F3,输出对应内容
        Console.ReadKey();
    } 
    static void F1(int i) => Console.WriteLine("我是F1:"+i);
    static void F2(int i) => Console.WriteLine("我是F2:" + i);
    static void F3(int i) => Console.WriteLine("我是F3:" + i);
}
// 定义委托
delegate void Mydel(int i);

八、事件:委托的安全封装(实战核心)

事件(Event)是委托的封装 ,在委托的基础上加了event关键字,让委托的使用更安全 ------这是WinForm、WPF、ASP.NET中事件驱动编程 的核心,比如按钮的点击事件Click

8.1 事件的基础实现(实战示例)

以"本命年提醒"为例,当Person的Age被赋值为12的倍数时,触发"本命年"事件,执行对应的方法:

csharp 复制代码
// 定义实体类,包含事件
class Person
{
    private int age;
    public int Age {
        get => this.age;
        set
        {
            this.age = value;
            // 当年龄是12的倍数时,触发事件
            if(value%12==0) OnBenMingNian?.Invoke(); // 空判断简化写法
        }
    }
    // 定义事件:event + 委托类型 + 事件名(常用OnXXX命名)
    public event Action OnBenMingNian;
}
// 主程序调用
class Program
{
    static void Main(string[] args)
    {
        Person p1 = new Person();
        p1.OnBenMingNian += BMN; // 订阅事件:+= 添加事件处理方法
        p1.Age = 5;  // 不触发事件,输出5
        Console.WriteLine(p1.Age);
        p1.Age = 24; // 触发事件,输出"本命年到了"+24
        Console.WriteLine(p1.Age);
        p1.Age = 55; // 不触发事件,输出55
        Console.WriteLine(p1.Age);
        Console.ReadKey();
    } 
    // 事件处理方法:参数和返回值必须和事件的委托类型一致
    static void BMN() => Console.WriteLine("本命年到了");
}

8.2 事件vs委托:核心区别

很多小伙伴会混淆事件和委托,核心区别就在于event关键字,加了之后会有访问限制

  1. 委托:可在外部直接赋值(=)、调用,灵活性高但不安全;
  2. 事件:在外部只能通过+=订阅、-=取消订阅,不能直接赋值、不能直接调用,只能在定义事件的类内部触发,更安全。

本质 :反编译后会发现,事件是由一个私有的委托变量 + add方法 + remove方法 组成的,+=对应调用add方法,-=对应调用remove方法。

8.3 经典面试题:接口中可以定义什么?

答案 :接口中可以定义方法、事件、属性、索引器
原因 :因为这四个元素的本质都是方法------属性是get/set方法,索引器是this的get/set方法,事件是add/remove方法,所以接口中可以定义它们(接口只能定义成员签名,不能实现)。

九、文末总结&敲代码建议

今天的内容覆盖了C#从项目实战核心语法的关键知识点,核心重点总结为3点:

  1. 多项目开发:核心是抽离类库、正确添加引用,记住"只有主项目的配置文件生效";
  2. 委托-Lambda-扩展方法:三者是一套体系,委托是基础,Lambda是简化委托的语法糖,扩展方法结合Lambda实现了Linq的核心功能;
  3. 事件 :是委托的安全封装,核心是event关键字,外部只能订阅/取消订阅,是事件驱动编程的基础。

最重要的建议一定要敲代码!文中的所有代码示例都能直接复制到控制台运行,先跑通,再逐行修改、调试(比如改Lambda的写法、改事件的触发条件),只有动手,才能把知识点从"看懂"变成"会用"。

这些知识点是C#进阶的基础,后续的Linq、异步编程(async/await)、设计模式(比如观察者模式)都离不开它们,收藏起来,反复看、反复敲,一定能吃透!

最后,想问大家:你在C#开发中,对委托、Lambda、事件的使用有哪些疑问?或者遇到过哪些坑?评论区聊聊~


关注我,后续继续分享C#实战干货、面试考点,从入门到进阶,一起加油~

相关推荐
SunflowerCoder4 小时前
基于插件化 + Scriban 模板引擎的高效 HTTP 协议中心设计
http·c#
青云计划7 小时前
知光项目用户关系模块
c#·linq
m5655bj7 小时前
使用 C# 修改 PDF 页面尺寸
java·pdf·c#
专注VB编程开发20年7 小时前
c#模仿内置 Socket.Receive(无需 out/ref,直接写回数据)
开发语言·c#
bugcome_com8 小时前
【零基础入门】C# 核心教程:从 HelloWorld 到入门精髓
c#
JQLvopkk8 小时前
C# 实现Http Json格式 Post 、Get 方法请求 winform服务器
http·c#·json
JQLvopkk8 小时前
C# 实践AI 编码:Visual Studio + VSCode 组合方案
人工智能·c#·visual studio
暖馒8 小时前
深度剖析串口通讯(232/485)
开发语言·c#·wpf·智能硬件
Traced back19 小时前
WinForms 线程安全三剑客详解
安全·c#·winform