• C# 委托浅析


    C# 中的委托(Delegate)类似于 C 或 C++ 中函数的指针。委托(Delegate) 是存有对某个方法的引用的一种引用类型变量。引用可在运行时被改变。

    委托(Delegate)特别用于实现事件和回调方法。所有的委托(Delegate)都派生自 System.Delegate 类。

    委托的声明(没有方法体的函数加上delegate关键字):

            /// <summary>
            /// 无参委托
            /// </summary>
            public delegate void DelegaetNoReturnPara();
            /// <summary>
            /// 有参委托
            /// </summary>
            /// <param name="num"></param>
            public delegate void DelegatePara(int num);
            /// <summary>
            /// 有参带返回值的委托
            /// </summary>
            /// <param name="num"></param>
            public delegate int DelegateParaNumber(int num);
            /// <summary>
            /// 泛型委托
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="t"></param>
            /// <returns></returns>
            public delegate T DelegateGeneric<T>(T t);

    我们用反编译工具查看一下

     用反编译工具可以看到委托是一个密闭的方法并且继承于[System.Runtime]System.MulticastDelegate,委托有一个构造函数,带有Invoke、BeginInvoke、EndInvoke三个方法。

    委托既然是类,那我们也可对其进行实例化

            public static void GetData(int num)
            {
                Console.WriteLine($"调用了{nameof(GetData)}方法");
            }
    
            public delegate void DelegatePara(int num);
    
            static void Main(string[] args)
            {
                Customer.GetData(1);
    //委托的实例化必须传递一个方法,这个方法的返回值和参数必须和实例化的这个委托一致
    //把方法包装成变量,Invoke的时候自动执行方法 DelegatePara noReturnPara
    = new DelegatePara(Customer.GetData);
    noReturnPara.Invoke(1);//调用委托
    noReturnPara(1);
    }

     在这里可能有同学会想,为什么不直接调用方法呢?ok,接着往下看.

    有一个student类和一个获取集合对象的一个方法.

    public class Student
    {
        public string name { get; set; }
        public int age { get; set; }
        public string sex { get; set; }
    }
    
    public async Task<List<Student>> GetAllStudents()
    {
        var result = new List<Student>();
        for (int i = 1; i < 10; i++)
        {
            result.Add(new Student
            {
                age = 17 + i,
                name = $"橘猫{i}",
                sex = i / 2 == 0 ? "" : ""
            });
        }
        return result;
    }

    假如现在有三个判断条件筛选集合中的内容

    /// <summary>
    /// 查询name的长度大于2的同学
    /// </summary>
    /// <returns></returns>
    public async Task<List<Student>> GetStudents()
    {
        var result = new List<Student>();
        var data = await this.GetAllStudents();
        foreach (var item in data)
        {
            if (item.name.Length > 2)
                result.Add(item);
        }
        return result;
    }
    /// <summary>
    /// 查询年龄大于18岁的同学
    /// </summary>
    /// <returns></returns>
    public async Task<List<Student>> GetStudents()
    {
        var result = new List<Student>();
        var data = await this.GetAllStudents();
        foreach (var item in data)
        {
            if (item.age > 18)
                result.Add(item);
        }
        return result;
    }
    /// <summary>
    /// 查询年龄大于18岁并且是女生的同学
    /// </summary>
    /// <returns></returns>
    public async Task<List<Student>> GetStudents()
    {
        var result = new List<Student>();
        var data = await this.GetAllStudents();
        foreach (var item in data)
        {
            if (item.age > 18 && item.sex.Equals(""))
                result.Add(item);
        }
        return result;
    }

    三个判断条件要写三个方法是不是太冗余了,我们直接写在一个方法里,根据条件类型来筛选数据

    /// <summary>
    /// 根据type来查询
    /// </summary>
    /// <returns></returns>
    public async Task<List<Student>> GetStudents(int type)
    {
        var result = new List<Student>();
        var data = await this.GetAllStudents();
        switch (type)
        {
            case 1:
                foreach (var item in data)
                {
                    if (item.name.Length > 2)
                        result.Add(item);
                }
                break;
            case 2:
                foreach (var item in data)
                {
                    if (item.age > 18)
                        result.Add(item);
                }
                break;
            case 3:
                foreach (var item in data)
                {
                    if (item.age > 18 && item.sex.Equals(""))
                        result.Add(item);
                }
                break;
            default:
                Console.WriteLine("查询的类型有误,请重新输入");
                break;
        }
        return result;
    }

    这样的话行是行,但是如果在多加一个条件呢?那么我们可不可以将判断逻辑传到方法里,只需返回true或false就行了.

    //定义委托
    public
    delegate bool judge(Student student);
    //用来判断姓名
    public bool judgeName(Student student) { return student.name.Length > 2; }
    //用来判断年龄
    public bool judgeAge(Student student) { return student.age > 18; }
    //用来判断年龄和性别
    public bool judgeAgeAndSex(Student student) { return student.age > 18 && student.sex.Equals(""); }
    /// <summary>
    /// 将判断逻辑传递过来
    /// 委托解耦,减少重复代码,将公共逻辑当成变量传递过来
    /// </summary>
    /// <returns></returns>
    public async Task<List<Student>> GetStudents(judge judge)
    {
        var result = new List<Student>();
        var data = await this.GetAllStudents();
        foreach (var item in data)
        {
            if (judge.Invoke(item))
                result.Add(item);
        }
        return result;
    }

    有同学会说,你这代码也不精简,为什么不用lamda.这里的话主要是想告诉大家委托的作用,侧重点不同,望理解。

    多播委托:

    public delegate void DelegatePara(int num);
    //这个委托实例只包含一个方法
    DelegatePara noReturnPara = new DelegatePara(Customer.GetData);
    //+= 为委托实例按顺序增加方法,形成方法链,依次执行
    DelegatePara noReturnPara = new DelegatePara(Customer.GetData);
    noReturnPara += new DelegatePara(Customer.GetData);
    noReturnPara += new DelegatePara(new DelegateInstance().GetData);
    noReturnPara.Invoke(1);//调用委托
    //-= 为委托实例移除方法,从方法链的底部开始匹配,遇到第一个完全吻合的,移除且只移除一个
    noReturnPara += new DelegatePara(Customer.GetData);
    noReturnPara += new DelegatePara(new DelegateInstance().GetData);//这个去不掉,因为不是同一个实例中的方法
    //循环获得委托中的实例
    foreach (var item in noReturnPara.GetInvocationList())
    {
        item.DynamicInvoke(1);//调用委托
    }

    多播委托如果带返回值,结果以最后的为准。

    事件:是带event关键字的委托实例,event可以限制变量被外部调用/直接赋值

    public delegate void HandleEvent(int num);
    public event HandleEvent handleEvent;
    public void DelegateEvent()
    {
        handleEvent = new HandleEvent(Customer.GetData);
        handleEvent.Invoke(1);
    }

    如果不是在本身所在的类中调用(事件)会如何呢?

     很明显报错了

    只能像下面这样写

    DelegateInstance delegateInstance = new DelegateInstance();
    delegateInstance.handleEvent += new DelegateInstance.HandleEvent(Customer.GetData);
    Console.ReadKey();

    这里需要说一下:Event+委托的一个实例,加上一个event关键字限制了外部调用权限,保证其安全(不能再其它类中调用和赋值,只能用来添加和移除注册方法),在本身类中可以调用和赋值,但是在子类中也不可以调用和赋值.

    委托与事件的区别和联系:委托是一个类型(委托的本质是一个类);事件是委托类型的一个实例.

    ok,今天先到这儿。如有不足的地方,望见谅。

  • 相关阅读:
    cocos代码研究(20)Widget子类LoadingBar学习笔记
    cocos代码研究(19)Widget子类ImageView学习笔记
    cocos代码研究(18)Widget子类Slider学习笔记
    cocos代码研究(17)Widget子类RadioButtonGroup学习笔记
    cocos代码研究(16)Widget子类RadioButton学习笔记
    cocos代码研究(15)Widget子类CheckBox学习笔记
    cocos代码研究(14)Widget子类Button学习笔记
    LeetCode:路径总和【112】
    LeetCode:平衡二叉树【110】
    LeetCode:二叉树的层次遍历||【107】
  • 原文地址:https://www.cnblogs.com/zhangnever/p/12864885.html
Copyright © 2020-2023  润新知