C#中的委托与事件笔记——基础

一、委托
  1. 委托的作用

    • 作为函数传参:包括回调函数(如 MyHeavyJob 方法执行完成后调用回调函数通知调用者)和在 LINQ 中传递规则(如 Select 方法对数组元素进行定制操作、PickOne 方法根据规则从两个数中选一个、Where 方法根据规则筛选元素)。

    • 声明事件:基于委托声明事件,如按钮的点击事件。

  2. 委托的声明与使用

    • 声明:在函数声明前加 delegate 关键字,类似接口中声明函数(只声明不实现)。

    • 实例化:实例化委托时需传递一个与委托函数签名相同的函数。

    • 调用:可将委托当作函数直接调用(如 fo())或使用 Invoke 方法调用。

    • 注册与删除方法:可以注册多个方法,注册顺序会影响调用顺序;也可以删除已注册的方法。

    • 判断是否为空:可通过判断委托是否等于 null 来确定其是否指向任何函数。

    委托本质上是一种引用类型,可以实例化一个委托,实例化时需要传入一个与委托签名匹配的函数,之后即可调用该委托

    delegate void Foo();
    void MyFunc()
    {
        Console.WriteLine("hello");
    }
    var foo = new Foo(MyFunc);
    // 虽然委托实例化时需要传入一个函数,但委托依然可以为空,如:
    // foo -= MyFunc; // 删除 MyFunc 方法,此时foo即为一个空的委托,调用时会报错 NullReferenceException
    // 一个委托可以注册多个方法,调用时会依次调用注册的函数
    // foo += MyFunc; // 此时foo中已经注册了两次 MyFunc 方法,调用时会输出两次hello
    foo(); // 调用委托
    // fooo.Invoke(); // 也可以使用 Invoke 方法调用
    

    带有返回值的委托在调用时只会返回最后注册的函数的返回值

  3. 经典使用案例

    1. 回调函数
    delegate void Callback();
    
    void MyHeavyJob(Callback callback)
    {
        Thread.Sleep(1500);
        callback();
    }
    
    void MyCallback()
    {
        Console.WriteLine("Job done");
    }
    
    MyHeavyJob(MyCallback);
    

    运行后1.5s后输出 “Job done”

    1. 策略模式,通过委托将“决策逻辑”(判断规则)作为参数传递
    delegate bool MyRule(int x, int y);
    
    int PickOne(int a, int b, MyRule rule)
    {
        if (rule(a, b)) return a;
        else return b;
    }
    
    bool MySpecialRule1(int x, int y)
    {
        return true;
    }
    
    bool MySpecialRule2(int x, int y)
    {
        return false;
    }
    
    Console.WriteLine(PickOne(10, 20, MySpecialRule1));
    Console.WriteLine(PickOne(10, 20, MySpecialRule2));
    

    运行后控制台输出 “10” 和 “20”

  4. 泛型委托

    • C#支持泛型委托

    • 为避免每次都声明委托,可使用 .NET 提供的泛型强类型委托,如 Action(无返回值)和 Func(有返回值)。

    • ActionFunc 有多个泛型参数版本,最多支持 16 个参数。

  5. 匿名委托与 Lambda 表达式

    • 匿名委托:无需实际声明方法,直接编写匿名委托,符合相应委托要求即可。
      PickOne(10, 20, delegate (int a, int b) {return true;});
      
    • Lambda 表达式:是特殊的委托,最终会被转换为 ActionFunc。在较新版本的 C# 中,编译器可自动推测 Lambda 表达式对应的强委托类型;在旧版本中,需主动写明委托类型。
      PickOne(10, 20, (a, b) => true);
      
二、事件
  1. 事件的本质:事件是特殊的委托,为了解决委托使用时需在实例化时立即注册方法的问题。

  2. 事件的声明与使用

    • 声明:先有委托类型,然后使用 event 关键字加上委托类型和事件名称来声明事件。事件通常作为类的成员出现。

      delegate void MyDelegate(); // 委托类型声明
      
      event MyDelegate MyEvent; // 实例
      
    • 实例化与调用:在类的实例中,通过事件名称添加事件处理程序(如使用 Lambda 表达式)。
      事件作为类的成员时只能在类的内部进行调用,调用时直接调用MyEvent(),或者使用 ?.Invoke() 避免因未注册事件而报错。

  3. 事件的常见使用场景

    • 当类中的某些成员(如属性、容器中的元素)发生变化时,希望外部能够观测到这些变化,可在类中声明事件,外部通过注册事件处理程序来观测。

      例如
      在自定义类中声明 MyValueChange 事件,当 MyValue 属性变化时触发该事件;
      ObservableCollection 集合有 CollectionChanged 事件,当对其进行增加、删除等操作时会触发该事件。

  4. 标准 .NET 事件模式

    • 委托类型:.NET 提供了 EventHandler 委托,返回值是 void,传参为 object senderEventArgs e,用于处理常见的事件,如界面上的按钮点击事件、集合变化事件等。

    • 事件参数类型:EventArgs 是一个基类,里面没有字段,可创建其子类来传递更多有效信息。不同的事件可能对应不同的 EventArgs 子类,如 ButtonClick 事件对应 RoutedEventArgs,其中包含事件相关的详细信息(如按下哪个按钮、事件是否被处理等)。

    • 命名规范

      ▪ 事件命名:推荐使用名词 + 动词被动形式,且动词被动最好以 ed 结尾,如 CollectionChangedPropertyChangedEventRaised 等;也有一些常见事件不严格遵循此规则,如 ButtonClick 事件、KeyDown 事件等。

      ▪ 事件处理方法命名:一般在事件名称前加 On(表示事件发生的那一刻,如 OnPropertyChanged)或 Raise(后面可不用被动语态,如 RaisePropertyChange)。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值