【Unity|C#】基础篇(8)——委托(Delegate)/ 事件(Event)

【学习资料】

        > 在线文档

            官方文档:https://docs.microsoft.com/zh-cn/dotnet/csharp/

            菜鸟教程(高级教程)https://www.runoob.com/csharp/csharp-tutorial.html

        > 视频教程

            腾讯学院、Siki学院

        > 书籍 

    《C#图解教程》(第13~14章)https://www.cnblogs.com/moonache/p/7687551.html

【学习内容】 

  > 菜鸟教程:高级教程部分(委托、事件)

  > 《C#图解教程》:第13~14章

  > 委托与事件详解Part1:http://www.tracefact.net/tech/009.html

  > 委托与事件详解Part2:http://www.tracefact.net/tech/029.html 


【委托Delegate】

  • 定义
    • 类似C++的函数指针,delegate相当于是 指向某对象的某个函数,引用类型变量
    • 委托和类一样,也需要 声明、创建、赋值
      • 声明委托类型(像类一样声明一个委托类型)  public delegate [函数返回值] 委托名称(参数1,参数2...);
      • 创建委托变量  MyDelegate opt = new MyDelegate(AddNum);  (实际绑定的是:this.AddNum)
      • 重新赋值  opt = MultNum;  (实际绑定的是:this.MultNum)
    • 执行委托:就是执行引用的 委托函数
    • 委托可以:动态修改引用的函数
    • // 声明:一个可以指向 返回类型为int,带2个int参数的 函数
      public delegate int MyDelegate(int a, int b);
      
      // 相加函数
      public int AddNum(int a, int b)
      {
          return (a + b);
      }
      // 相乘函数
      public int MultNum(int a, int b)
      {
          return (a * b);
      }
      
      void Start()
      {
          // 创建:指向不同函数的委托类型
          MyDelegate opt = new MyDelegate(AddNum);
          Debug.Log(opt(2, 5)); // 执行委托函数: 7
      
          // 运行时,可以改变赋值
          opt = MultNum;
          Debug.Log(opt(2, 5)); // 执行委托函数: 10
      }
  • 委托多播
    • 创建的委托类型变量 opt 可以存储 函数调用列表(绑定多个委托函数)
    • 通过 "+",或"+="添加 一个函数引用
    • 通过 "-",或"-="  :移除 某个函数的引用
    • 执行委托:会执行所有添加绑定的 委托函数
    • public delegate void MyDelegate(int a, int b);
      
      // 输出相加结果
      public void PrintAddNum(int a, int b)
      {
          Debug.Log("PrintAddNum:" + (a + b));
      }
      // 输出相乘结构
      public void PrintMultNum(int a, int b)
      {
          Debug.Log("PrintMultNum:" + (a * b));
      }
      
      void Start()
      {
          // 创建:指向不同函数的委托类型
          MyDelegate opt = new MyDelegate(PrintAddNum);
      
          // 通过+=添加委托函数
          // 也可以直接相加 opt = PrintAddNum + PrintMultNum;
          opt += PrintMultNum;
          // 执行委托,会按添加的顺序,分别执行PrintAddNum和PrintMultNum
          opt(2, 5);
      
          // 通过-=移除对某函数的引用
          opt -= PrintAddNum;
          // 再次执行委托,只执行了PrintMultNum
          opt(2, 5);
      }
      
      // 输出
      //  PrintAddNum: 7
      //  PrintMultNum: 10
      //  PrintMultNum: 10
  • 委托绑定函数及执行原理
    • 绑定委托函数
      • 可以通过:“=”、“+=”、“-=”、“+”、“-”,来重新给委托绑定函数,或添加删除函数
      • 绑定普通函数:同时会将对象(this)引用存储起来
      • 绑定静态函数:无需存储对象(this)引用
      • class Person
        {
            public delegate void MyDelegate(int a, int b);
            public MyDelegate myDelegate;
        
            // 普通函数,绑定时需要有明确对象(this)
            public void Func1(int a, int b) { ... }
            // 静态函数,通过类名.Func2绑定
            public static void Func2(int a, int b) { ... }
        }
        
        void Start()
        {
            Person myPerson = new Person();
        
            //myPerson.myDelegate += Person.Func1;  // 报错,Func1需要对象(this)
            myPerson.myDelegate += Person.Func2;
        
            myPerson.myDelegate += myPerson.Func1;
            //myPerson.myDelegate += myPerson.Func2; // 报错,Func2是静态函数
        }
    • 执行方式
      • 通过"+" 或 "+=" 的顺序,依次执行绑定的委托函数
      • 普通函数:相当于 调用 绑定对象引用(this)的函数,包含了对象的this引用
      • 静态函数:相当于 调用 绑定的委托函数;
      • // 声明委托类型
        public delegate void MyDelegate();
        
        class Person
        {
            public string name;
            public void PrintName()
            {
                Debug.Log(name);
            }
        }
        
        void Start()
        {
            // 创建2个对象
            Person myPerson = new Person();
            Person myPerson2 = new Person();
            myPerson.name = "Alice";
            myPerson2.name = "Bob";
        
            // 创建委托变量
            MyDelegate myDelegate = new MyDelegate(myPerson.PrintName);
            // 多播:委托函数
            myDelegate += myPerson2.PrintName;
        
            // 执行委托
            myDelegate();
        }
        
        // 输出
        //  Alice
        //  Bob

【事件】 

  • 定义 
    • 事件是对委托的封装,关系和字段与属性(Property) 一样
    • 事件是基于委托,所以首要声明委托,再定义事件
    • // 声明委托类型
      public delegate void MyDelegate(int a, int b);
      // 定义事件
      public event MyDelegate myEvent;
    • 原理
      • 实际上与属性一样,定义一个事件,会自动创建一个隐藏(private)的委托类型变量
      • 并包含了add_xxx("+=")和remove_xxx("-="),用来添加/删除 委托函数
    • 事件的执行:只能在定义事件的类内部执行
    • // 声明委托类型
      public delegate void MyDelegate(int a, int b);
      
      class Person
      {
          // 定义事件
          public event MyDelegate myEvent;
      
          public void FireEvent(int a, int b)
          {
              if (myEvent != null)
                  myEvent(a, b); // 必须在定义事件的类内部执行
          }
      }
      
      public void PrintAddNum(int a, int b)
      {
          Debug.Log("PrintAddNum:" + (a + b));
      }
      public void PrintMultNum(int a, int b)
      {
          Debug.Log("PrintMultNum:" + (a * b));
      }
      
      void Start()
      {
          Person person = new Person();
          person.myEvent += PrintAddNum;
          person.myEvent += PrintMultNum;
      
          //person.myEvent(2, 5); // 报错,无法在定义事件的类外部执行
          person.FireEvent(2, 5); // 正确
      }
    • 注:事件不能在类的外部使用赋值“=”,但是在类内部可以重新赋值“=”
    • 事件与委托一样,也支持多播:可以通过“+=”、“-=” 来添加事件、删除事件
  •  事件访问器(add / remove)
    • 事件与委托的关系,与 字段与属性的关系是一样的,所以也有访问器
    • 关键字: add  remove 
    • public event MyDelegate myEvent
      {
          add
          {
              ...    //执行 += 运算符的代码
          }
          remove
          {
              ...    //执行 -= 运算符的代码
          }
      }
  • 为什么使用事件?
    • 对委托的封装
      • 对外只能通过事件来 添加/删除 绑定的委托函数
      • 事件只能在定义事件的 类内部执行
    • 满足观察者模式(发布者-订阅者)
    • 当然不使用事件,也可以实现对委托的封装
      • 将委托定义为private,然后对外提供添加/删除委托函数的方法(相当于手动实现事件中的add_xxx和remove_xxx)
      • // 声明委托类型
        public delegate void MyDelegate(int a, int b);
        
        class Person
        {
            // 定义一个私有委托变量
            public MyDelegate myDelegate;
            // 添加
            public void AddEvent(MyDelegate d)
            {
                myDelegate += d;
            }
            // 删除
            public void RemoveEvent(MyDelegate d)
            {
                myDelegate -= d;
            }
            // 执行
            public void FireEvent(int a, int b)
            {
                if (myDelegate != null)
                    myDelegate(a, b); // 必须在定义事件的类内部执行
            }
        }
        
        public void PrintAddNum(int a, int b)
        {
            Debug.Log("PrintAddNum:" + (a + b));
        }
        public void PrintMultNum(int a, int b)
        {
            Debug.Log("PrintMultNum:" + (a * b));
        }
        
        void Start()
        {
            Person person = new Person();
        
            person.AddEvent(PrintAddNum);       // this.PrintAddNum
            person.RemoveEvent(PrintMultNum);   // this.PrintMultNum
        
            person.FireEvent(2, 5);
        }

猜你喜欢

转载自www.cnblogs.com/shahdza/p/12239296.html