[C# 基础] 委托 和 事件

学习自B站up主
链接: 学习参考资料B站小Joe.
在这里插入图片描述

1、委托上-如何自定义委托

理解委托在现实世界的字面意思
委托是引用类型,是一种类,全称委托类型
typeof(delegate).IsClass 判定
委托创建的实例可以存储1个或N个方法,这个委托实例可以间接调用Invoke方法
类型兼容
委托的声明(与函数指针的声明类似),在创建实例,Target目标方法
在这里插入图片描述
target = new MyDelegate( target 目标方法)
myDelegate = target ;直接赋值
调用直接invoke
间接调用myDeleget(target方法)
单播委托 单个 = ,方法会覆盖掉上一个方法
( = 表示重置了委托封装的引用列表) 这是一个很大的缺点
多播委托 +=
target += new MyDelegate( target1)
target += new MyDelegate( target2)
在这里插入图片描述
【注意】
在这里插入图片描述
动态调用委托封装的方法

C#类库提供了2个委托的类型
Action委托和Func委托在这里插入图片描述

2、委托下-Action和Func委托、委托的使用

在这里插入图片描述
委托类型的实例间接方法调用方法
使用委托当做参数传递到方法的用法,好处就是动态调用方法在这里插入图片描述
泛型委托Action和Func,T泛化的数据类型

 		public delegate void MyDelegateAction();
        public MyDelegateAction Action;
        //上面两行等于下面一行
        System.Action action; //没有参数列表
        System.Action<String> action //有参数列表

Action和Func委托的区别,有无返回值
Func<in,in,in,out>

过度使用委托缺点:
1、委托是一种方法级别的耦合,这种耦合是违反设计模式的
2、使用委托不当的话,可读性下降,debug难度上升
3、如果你把模板方法、回调方法、异步调用、多线程纠缠在一起,代码维护就是灾难级别的
4、委托使用不当会导致内存泄露
内存泄露原理
委托引用一个方法,如果这个方法是一个实例方法(非静态),那这个方法必然隶属于一个对象,那个这个对象就必须存在在内存当中,无论有无其它引用,这个对象内存就不能被释放。所以一旦委托间接调用某一个方法,就会造成内存泄露。
【不要过度使用】

委托作为方法的参数
模板方法和回调方法
模板方法,就相当于对不确定的地方,用委托传入参数
回调方法,动态去选择,去调用的方法

匿名函数:lambda表达式,不用别处声明,一遍声明一遍调用,inline函数,只调用一次

在这里插入图片描述
在这里插入图片描述

3、泛型-泛化的数据类型

泛化,特化,类型参数
在这里插入图片描述
使用array
在这里插入图片描述
通过协程,bool值来禁止非常规操作
在这里插入图片描述

4、事件的定义和事件模型的5个组成部分

在这里插入图片描述
事件是一种特殊形式的委托类型的字段或变量
在这里插入图片描述
事件是对象/类型的成员
在这里插入图片描述

事件模型5个组成部分和5个步骤在这里插入图片描述
事件参数列表可以没有参数
在这里插入图片描述
事件的拥有者:源头,一定是一个类!
事件:不会主动发生,由事件拥有者的内部逻辑触发,事件是一个工具,核心功能是通知,通知其他类对、象做出响应
事件解决了委托的问题,可以高内聚低耦合,只允许+=和-=,静止外部访问
事件的响应者:订阅事件的具体的类或对象
事件处理器:是处理事件的方法Method(此方法有限制),事件响应者的方法成员
订阅关系:+=和-=,要理解,解决了三个核心问题

类型兼容:事件处理器必须和事件相互匹配,遵循同一个规定,那就是委托类型
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

5、如何声明自定义的事件以及事件的完整/简略声明格式

事件的完整声明格式
Event事件是基于委托delegate的
1、类型兼容(委托约束)
2、存储方法的引用
委托是事件的底层基础
事件是委托的上层建筑

简略的声明格式,像字段的事件(语法糖)

在这里插入代码片
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class EventEx : MonoBehaviour
{
    //事件模型的5个组成部分
    //事件拥有者【类】
    //事件【event修饰】
    //事件响应者【类】
    //事件处理器【方法-受到约束】
    //事件的订阅关系【+=】

    Customer customer = new Customer();
    Waitor waitor = new Waitor();

    private void Start()
    {
        //事件是事件拥有者的Event成员
        //订阅关系
        customer.OnOrder += waitor.TakeAction; //先写方法名,然后,修复程序补完
        //customer.OnOrder();//报错,不能再外部直接调用
        customer.Order();//事件拥有者的内部逻辑,触发的事件
        customer.PayBill();
    }

}

//事件拥有者
public class Customer
{
    public float Bill { get; set; }
    public void PayBill()
    {
        Debug.Log("you have to pay the bill"+Bill);
    }

    //声明事件,点单的事件,完整声明格式
    //蔚来就会去存储,去引用那些服务员的事件处理器
    private OrderEventHandler orderEventHandler; //委托字段
    //需要同时具有add和remove
    public event OrderEventHandler OnOrder
    {
        add { orderEventHandler += value; } //添加事件处理器
        remove { orderEventHandler -= value; }
    }
    
    //简略声明格式
    //public event OrderEventHandler OnOrder;

    public void Order()
    {
        if (orderEventHandler!=null) //如果事件有人注册
        {
            OrderEventArgs e = new OrderEventArgs();
            e.CoffeeName = "Tingge";
            e.CoffeeSize = "Big";
            e.CoffeePrice = 22f;
            orderEventHandler(this,e);
        }
    }

}

//事件响应者
public class Waitor
{
    //自动修复的方法,完美匹配参数
    //事件处理器,通过内部逻辑触发
    internal void TakeAction(Customer _customer, OrderEventArgs _e)
    {
        float finalPrice = 0;
        switch (_e.CoffeeSize)
        {
            case "Small":
                finalPrice = _e.CoffeePrice; 
                break;
            case "Middle":
                finalPrice = _e.CoffeePrice+3;
                break;
            case "Big":
                finalPrice = _e.CoffeePrice+6;
                break;
        }
        _customer.Bill += finalPrice; //事件处理
    }
}




//2个为了事件的前期准备
//为OnOrder事件声明委托,注意命名规范,事件+EventHandler,2个参数(事件拥有者,点餐的具体事件信息)
public delegate void OrderEventHandler(Customer _customer , OrderEventArgs _e);

//存储和传递事件参数,事件信息、事件消息,属于EventArgs这个类
public class OrderEventArgs : System.EventArgs
{
    public string CoffeeName { get; set; }
    public string CoffeeSize { get; set; }
    public float CoffeePrice { get; set; }

}

在这里插入图片描述

事件:可以防止委托借刀杀人的情况发生
去掉event,答案还是正确,但是外界都可以访问它,也就是其他类对象可以访问,导致借刀杀人。
字段能做的,属性能做,属性能做的,字段不一定能做,保护字段
封装的概念
在这里插入图片描述

在这里插入图片描述

Q&A为什么要使用委托类型来声明事件

使用委托类型的实例去存储方法的引用,去存储这个事件处理器
事件响应者向事件拥有者,提供了一个与之匹配的事件的事件处理器后,存储事件处理器

链接: 学习参考资料B站小Joe.

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值