C#委托

1. 委托是什么?

  • 委托(Delegate) 是一种类型,专门用来存储、传递方法。
  • 本质上,委托是 方法的容器,可以把方法当作变量一样传递和调用。
  • 只要 返回值和参数类型 与委托匹配,就可以存入委托。

委托常用于回调、事件系统、解耦模块逻辑、延迟执行等场景。


2. 使用 delegate 委托的声明(不推荐)

委托的声明语法类似方法声明,只是前面加上 delegate 关键字:

delegate void MyFun();        // 无参无返回值委托声明
delegate int MyFun2(int a);   // 有参有返回值委托声明
delegate T MyFun3<T, K>(T v, K k);    //泛型委托声明 
  • 委托可以声明在 命名空间类内部
  • 同一作用域中,委托 不能重名,也不支持重载。
  • 委托的本质是类,可以通过实例化调用方法。

3. C#封装好 delegate 的委托(推荐)

Action<T1,...,Tn> action;       // 接收 T1~Tn 参数,最多 16 个参数,无返回
Func<T1,...Tn,TResult> func;  // 接收 T1~Tn 参数,最多 16 个参数,返回 TResult型值

当想要声明无返回的委托,使用Action。当想要声明有返回的委托,使用Func。


4. 委托的常用

  • 委托的声明在方法外 委托的执行在自定义方法里实现,调用自定义方法时会执行委托
  • 委托的执行要确认是否为null
  • 委托只声明没有实现,委托默认是null,委托的添加或删除在自定义方法里实现,调用自定义方法时会对委托进行添加或删除

4.1 委托常作为类的成员

class Custom
{
  public Action<int> action;
  public Func<int,int> func;
  public void Run()
  {
    // action 不为空时执行(action 不为null时 也可以使用 action(10)\action.Invoke(10) 执行 推荐使用 ?.Invoke)
    action?.Invoke(10);  
    // func 不为空时执行 (result 结果会等于返回的 num)
    int result = func?.Invoke(10); 
  }
}
class Test
{
  int value;
  int num = 5;
  public void TestMethod()
  {
    Custom custom = new Custom();
    // 通过 lambad 表达式 定义 Custom 类中委托 action 执行内容
    custom.action=(a)=>{
      value = a;
    };
    // 通过 lambad 表达式 定义 Custom 类中委托 func 执行内容
    custom.func=(a)=>{
      value = a;
      return num;
    };
    // 调用 Run 来触发委托
    custom.Run();
  }
}

4.2. 委托常作为函数参数

class Custom
{
    int value = 1;
    // 有参无返回方法
    public void CustomMethod1(int a)
    {
        value *= a;
        Console.WriteLine("CustomMethod1 value=" + value);
    }
    //有参有返回方法
    public int CustomMethod2(int a)
    {
        value *= a;
        Console.WriteLine("CustomMethod2 value=" + value);
        return value;
    }
    public void Run()
    {
        Test test = new Test();
        // 方法一 传入 已有的方法
        test.TestAction(CustomMethod1);
        test.TestFunc(CustomMethod2);
        // 方法二 传入 Lambda 表达式(推荐)
        test.TestAction(a => { 
          value += a; 
        });
        test.TestFunc(a => { 
          value += a; 
          return value; 
        });
    }
}
class Test
{
    //传入有参无返回委托
    public void TestAction(Action<int> callback)
    {
        int num = 10;
        callback(num); // 调用回调
    }
    //传入有参有返回委托
    public void TestFunc(Func<int,int> callback)
    {
        int num = 10;
        int result = callback(num); // 调用回调
        Console.WriteLine("TestFunc result=" + result);
    }
}

4.3. 多播委托(Multicast Delegate)

public class Test
{
  public void Method1(){}
  public void Method2(){}
  public void Method3(){}
  public int Method11(){ return 0; }
  public int Method22(){ return 1; }
  public int Method33(){ return 2; }
  Action action;
  Func<int> func;
  //添加委托
  public void AddActionFunc()
  {
    action += Method1;
    action += Method2;
    action += Method3;
    // 按顺序执行添加的方法
    action?.Invoke();
    func += Method11;
    func += Method22;
    func += Method33;
    // 按顺序执行添加的方法 但是只保留最后一个方法的返回值
    int result = func?.Invoke();  // result 结果为2
  }
  //移除委托(多次移除同一方法不会报错)
  public void RemoveActionFunc()
  {
    action -= Method1;
    action -= Method2;
    action -= Method3;
    func -= Method11;
    func -= Method22;
    func -= Method33;
  }
  //清空委托
  public void RemoveAllActionFunc()
  {
    action = null;
    func = null;
  }
}

5. 委托使用实例

  • 怪物死亡后,玩家要加10块钱,界面要更新数据,成就要累加怪物击杀数
class Monster
{
    //当怪物死亡时 把自己作为参数传出去
    public Action<Monster> deadDoSomething;
    //怪物成员变量 特征 价值多少钱
    public int money = 10;
    public void Die()
    {
        Console.WriteLine("怪物死亡");
        deadDoSomething?.Invoke();
        ////一般情况下 委托关联的函数 有加 就有减(或者直接清空)
        deadDoSomething = null;
    }
}
class Player
{
    private int myMoney = 0;
    public void MonsterDeadDoSomething(Monster m)
    {
        myMoney += m.money;
        Console.WriteLine("现在有{0}元", myMoney);
    }
}
class UI
{
    private int nowShowMoney = 0;
    public void AddCoin(Monster m)
    {
        nowShowMoney += m.money;
        Console.WriteLine("当前面板显示{0}元", nowShowMoney);
    }
}
class CJ
{
    private int nowKillMonsterNum = 0;
    public void AddNum(Monster m)
    {
        nowKillMonsterNum++;
        Console.WriteLine("当前累计击杀怪物{0}个", nowKillMonsterNum);
    }
}
class Program
{
    static void Main(string[] args)
    {
        Monster monster = new Monster();
        Player player = new Player();
        UI panel = new UI();
        CJ honor = new CJ();
        monster.deadDoSomething += player.MonsterDeadDoSomething;
        monster.deadDoSomething += panel.AddCoin;
        monster.deadDoSomething += honor.AddNum;
        monster.Die();
    }
}

在实际项目中,可以把玩家、界面、成就设计为单例类,怪物死亡时,在死亡方法中通过单例实例调用玩家、界面、成就对应方法

posted @ 2025-12-10 12:43  高山仰止666  阅读(1)  评论(0)    收藏  举报