zoukankan      html  css  js  c++  java
  • C# 委托 Delegate

    C# 委托 Delegate


    Delegate 类表示委托,委托是一种数据结构,它引用静态方法或引用类实例及该类的实例方法。

    委托的声明、实例化和调用

    声明

    以下实例声明为Del的委托,该委托采用字符串作为参数,并返回void的方法:

    public delegate void Del(string message);
    

    实例化

    Delegate的实例化和Class的实例化基本类似。

    以下实例是,创建一个Del委托的实例。因为该委托声明是:采用字符串作为参数,并返回void的方法,所以实例化该委托时需要传递一个符号条件的方法作为参数:

    Del handler = new Del(DelegateMethod);
    

    上面代码可以简写为以下代码:

    Del handler = DelegateMethod;//简写,只写方法就可以,不用使用new关键字
    

    为委托创建一个DelegateMethod 方法(该方法采用字符串作为参数,并返回void):

    void DelegateMethod(string message)
    {
        System.Console.WriteLine(message);
    }
    

    调用

    调用方式:

    Del handler = new Del(DelegateMethod);
    handler.Invoke("Hello World");
    

    简写方式(就像将DelegateMethod方法赋值给一个变量,然后通过传递参数的方式执行这个变量):

    Del handler = DelegateMethod;
    handler("Hello World");
    

    泛型委托--Func Action

    因为委托实际上就是一个方法的类型,所以 .NET Framework根据泛型内置了Action与Func两种形式的委托。

    Action

    Action是无返回值的泛型委托。

    • Action 表示无参,无返回值的委托。如下:
    public delegate void Action();
    
    • Action<int,string> 表示有传入参数int,string无返回值的委托
    • Action<int,string,bool> 表示有传入参数int,string,bool无返回值的委托
    • Action<int,int,int,int> 表示有传入4个int型参数,无返回值的委托
    • ·······

    Action至少0个参数,至多16个参数,无返回值。

    Func

    Func是有返回值的泛型委托。

    • Func 表示无参,返回值为int的委托
    public delegate TResult Func<out TResult>();
    
    • Func<object,string,int> 表示传入参数为object, string 返回值为int的委托
    • Func<object,string,int> 表示传入参数为object, string 返回值为int的委托
    • Func<T1,T2,,T3,int> 表示传入参数为T1,T2,,T3(泛型)返回值为int的委托
    • ·······

    Func至少0个参数,至多16个参数,根据返回值泛型返回。必须有返回值,不可void

    委托的意义

    解耦

    例如我们有这样一个需求,有一组学生数据,需要根据条件查询出相应的学生列表,在不使用委托的时候可能我们会写出以下的代码:

    学生类:

        public class Student
        {
            public string Name { get; set; }
            public int Age { get; set; }
            public string Email { get; set; }
    
            public Student(string name, int age, string email)
            {
                this.Name = name;
                this.Age = age;
                this.Email = email;
            }
    
            public Student() { }
    
            public void Study()
            {
                Console.WriteLine("我正在学习……");
            }
        }
    

    实现代码:

        List<Student> students = new List<Student>();
        students.Add(new Student("Oliver", 18, "123@qq.com"));
        students.Add(new Student("小白", 19, "123@sina.com"));
        students.Add(new Student("老K", 28, "123@163.com"));
        students.Add(new Student("土豆", 16, "232@sina.com"));
        students.Add(new Student("番茄", 33, "fanqie@qq.com"));
        students.Add(new Student("小红", 22, "24424@163.com"));
        students.Add(new Student("黎明", 18, "2232@126.com"));
        students.Add(new Student("黄昏", 21, "huanghun@qq.com"));
        
        {
            //找出20岁以上的学生
            List<Student> newList1 = new List<Student>();
            foreach (var item in students)
            {
                if (item.Age > 20)
                {
                    newList1.Add(item);
                }
            }
            Console.WriteLine("查询到条数:" + newList1.Count);
        
        
            //找出邮箱长度大于10位的学生
            List<Student> newList2 = new List<Student>();
            foreach (var item in students)
            {
                if (item.Email.Length > 10)
                {
                    newList2.Add(item);
                }
            }
            Console.WriteLine("查询到条数:" + newList2.Count);
        
        
            //找出年龄在20岁以上并且邮箱长度大于10位的学生
            List<Student> newList3 = new List<Student>();
            foreach (var item in students)
            {
                if (item.Age > 20 && item.Email.Length > 10)
                {
                    newList3.Add(item);
                }
            }
            Console.WriteLine("查询到条数:" + newList3.Count);
        }
    

    经过我们的分析,发现下面的代码在上面重复了3次,都是循环然后进行逻辑判断。

        foreach (var item in students)
        {
            if (....)
            {
                newList.Add(item);
            }
        }
    

    然后我们通过使用委托,将逻辑封装到一个方法中,然后将方法传递进去,这样我们就减少耦合:

        //定义一个条件委托
        delegate bool WhereDelegate(Student s);
    
        //判断年龄是否大于20
        bool GetAgeThan(Student s)
        {
            return s.Age > 20;
        }
        //判断邮箱长度是否大于10
        bool GetEmailLen(Student s)
        {
            return s.Email.Length > 10;
        }
        //判断是否年龄在20岁以上并且邮箱长度大于10位
        bool GetAgeThanAndEmailLen(Student s)
        {
            return s.Age > 20 && s.Email.Length > 10;
        }
        /// <summary>
        /// 根据逻辑条件筛选数据
        /// </summary>
        /// <param name="list">列表</param>
        /// <param name="where">逻辑条件</param>
        /// <returns>筛选结果</returns>
        List<Student> StudentWhere(List<Student> list, WhereDelegate where)
        {
            List<Student> newList = new List<Student>();
            foreach (var item in list)
            {
                if (where(item))
                {
                    newList.Add(item);
                }
            }
            return newList;
        }
    

    调用:

        //找出20岁以上的学生
        List<Student> newList1 = StudentWhere(students, GetAgeThan);
        Console.WriteLine("查询到条数:" + newList1.Count);
        //找出邮箱长度大于10位的学生
        List<Student> newList2 = StudentWhere(students, GetEmailLen);
        Console.WriteLine("查询到条数:" + newList2.Count);
        //找出年龄在20岁以上并且邮箱长度大于10位的学生
        List<Student> newList3 = StudentWhere(students, GetAgeThanAndEmailLen);
        Console.WriteLine("查询到条数:" + newList3.Count);
    

    多播委托

    包含多个方法的委托成为多播委托,调用多播委托,可以按照顺序连续调用多个方法。

    多播委托可以用运算符"+"和"+="给委托添加方法调用,同样也可以用运算符"-"和"-="给委托删除方法调用

        //多播委托
        Student s = new Student();
        Action action = s.Study;//初始化并添加一个学习的方法
        action += s.Sports;//添加一个运动的方法
        action();//执行
        Console.WriteLine("··············");
        action -= s.Sports;//减少一个运动的方法
        action();
        //输出:
        //我正在学习
        //我正在运动
        //··············
        //我正在学习
    

    事件

    声明:

    public event Action OnSports;//Action 是一个委托
    

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

    事件委托的区别:

    • 委托是一个Class(如Student)
    • 而事件是一个委托的实例(如:new Student())

    事件委托实例的区别:

    • 事件不能赋值(=)操作,而委托实例可以。
    • 事件不能直接调用,如:s.OnSports(),而委托实例可以。
    • 事件时一个特殊的委托实例。

    我们修改下Student代码,为它添加一个委托的实例对象,一个事件来比较两者的区别:

        public class Student
        {
            public Action OnStudy;//定义了一个委托的实例
            public event Action OnSports;//定义了一个事件
    
            public string Name { get; set; }
            public int Age { get; set; }
            public string Email { get; set; }
    
            public Student(string name, int age, string email)
            {
                this.Name = name;
                this.Age = age;
                this.Email = email;
            }
    
            public Student() { }
    
            public void Study()
            {
                if (this.OnStudy != null)
                {
                    this.OnStudy();//调用委托实例
                }
                Console.WriteLine("我正在学习");
            }
    
            public void Sports()
            {
                if (this.OnSports != null)
                {
                    this.OnSports();//调用事件实例
                }
                Console.WriteLine("我正在运动");
            }
        }
    

    在Student外边添加下面代码:

        Student s = new Student();
        s.OnSports = null;//编译错误,提示:事件“Student.OnSports”只能出现在 += 或 -= 的左边(从类型“Student”中使用时除外) 
        s.OnSports();//编译错误,提示:事件“Student.OnSports”只能出现在 += 或 -= 的左边(从类型“Student”中使用时除外) 
    
        s.OnStudy = null;//正常运行
        s.OnStudy();//正常运行
    
  • 相关阅读:
    xcode Git
    The OAuth 2.0 Authorization Framework
    Apache Commons 工具集介绍
    遍历map
    ClassLoader 提供了两个方法用于从装载的类路径中取得资源:
    转:mysql 索引
    StringBuilder与StringBuffer
    第四次作业
    Java Class 与 Object
    软件测试中条件覆盖,路径覆盖,语句覆盖,分支覆盖的区别
  • 原文地址:https://www.cnblogs.com/haowuji/p/9478162.html
Copyright © 2011-2022 走看看