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();//正常运行
posted @ 2018-08-14 21:55  下-个路口  阅读(416)  评论(0编辑  收藏  举报