关键词搜索

源码搜索 ×
×

C#委托与事件学习笔记

发布2020-12-14浏览315次

详情内容

本笔记摘抄自:https://www.cnblogs.com/leslies2/archivehttps://cdn.jxasp.com:9143/image/2012/03https://cdn.jxasp.com:9143/image/22https://cdn.jxasp.com:9143/image/2389318.html,记录一下学习过程以备后续查用。
一、委托类型的来由

在使用C语言的年代,整个项目中都充满着针指的身影,那时候流行使用函数指针来创建回调函数,使用回调可以把函数回调给程序中的vb.net教程
另一个函数。但函数指针

只是简单地把地址指向另一个函数,并不能传递其他额外信息
在.NET中,大部分时间里都没有指针的身影,因为指针被封闭在内部函数当中。可是回调函数却依然存在,它是以委托的方式来完成的。委托可以被视为一个更

高级的指针,它不仅仅能把地址指向另一个函数,而且还能传递参数、返回值等多个信息。系统还为委托对象自动生成了同步、异步的调用方式,java基础
开发人员使用

BeginInvoke、EndInvoke方法就可以抛开Thread而直接使用多线程调用 。

二、建立委托类

使用delegate可以直接创建委托类型,当进行系统编译时,系统就会自动生成此类型,可以使用delegate void MyDelegate()方式创建一个委托类。

  class Program
    {
        delegate void MyDelegate();
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World.");
            Console.Read();
        }
    }

    使用ILDASM.exe观察委托成员,可以看到它继承了System.MulticastDelegate类,并自动生成BeginInvoke、EndInvoke、Invoke 等三个常用方法。

    在这里插入图片描述

    Invoke 方法是用于同步python基础教程
    调用委托对象的对应方法,而BeginInvoke、EndInvoke是用于以异步方式调用对应方法。

        public class MyDelegate:MulticastDelegate
         {
             //同步调用委托方法
             public virtual void Invoke();
             //异步调用委托方法
             public virtual IAsyncResult BeginInvoke(AsyncCallback callback,object state);
             public virtual void EndInvoke(IAsyncResult result);
         }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    MulticastDelegate是System.Delegate的子类,它是一个特殊类,编译器和其他工具可以从此类派生,但是自定义类不能显式地从此类进行派生。它支持多路

    广播委托,并拥有一个带有链接的委托列表。在调用多路广播委托时,系统将按照调用列表中的委托出现顺序来同步调用这些委托。

    MulticastDelegate具有两个常用属性:Method、Target。其中Method用于获取委托所表示的方法,Target用于获取当前调用的类实例。

    MulticastDelegate有以下几个常用方法:

    方法名称说明
    Clone创建委托的浅表副本。
    GetInvocationList按照调用顺序返回此多路广播委托的调用列表。
    GetMethodImpl返回由当前的 MulticastDelegate 表示的静态方法。
    GetObjectData用序列化该实例所需的所有数据填充 SerializationInfo 对象。
    MemberwiseClone创建当前 Object 的浅表副本。
    RemoveImpl调用列表中c#教程移除与指定委托相等的元素

    MulticastDelegate与Delegate给委托对象建立了强大的支持。

    三、委托使用方式

    3.1 简单的委托

    当建立委托对象时,委托的参数类型必须与委托方法相对应。只要向建立委托对象的构造函数中输入方法名称example.Method,委托就会直接绑定此方法。

    使用myDelegate.Invoke(string message),就能显式调用委托方法。

    但在实际的操作中,我们无须用到Invoke方法,而只要直接使用myDelegate(string message),就能调用委托方法。

    下面代码演示简单的委托:

        class Program
        {
            delegate void MyDelegateVoid(string message);
    
            public class Example
            {
                public void ShowMessage(string message)
                {
                    Console.WriteLine(message);
                }
            }
    
            static void Main(string[] args)
            {
                #region 简单的委托
                Example example = new Example();
                MyDelegateVoid myDelegateVoid = new MyDelegateVoid(example.ShowMessage);
                myDelegateVoid("Hello World");
                Console.Read();
                #endregion
            }
        }
    
      10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    运行结果如下:
    
    • 1

    在这里插入图片描述

    3.2 带返回值的委托

    当建立委托对象时,委托的返回值必须与委托方法相对应。

    下面代码演示带返回值的委托:

    
        class Program
        {
            delegate string MyDelegateString(string message);
    
            public class Example
            {
                public string SayHi(string name)
                {
                    return "Hello " + name;
                }
            }
    
            static void Main(string[] args)
            {
                #region 带返回值的委托
                Example example = new Example();
                MyDelegateString myDelegateString = new MyDelegateString(example.SayHi);
                string message = myDelegateString("Atomy");
                Console.WriteLine(message);
                Console.Read();
                #endregion
            }
        }
    
    
      10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    运行结果如下:
    
    • 1

    在这里插入图片描述

    3.3 多路广播委托
    在第二节前曾经提过,委托类继承于MulticastDelegate,这使委托对象支持多路广播,即委托对象可以绑定多个方法。

    下面代码演示多路广播委托:

        class Program
        {
            delegate double MyDelegateDouble(double message);
    
            public class Example
            {
                public double Ordinary(double price)
                {
                    double price1 = 0.95 * price;
                    Console.WriteLine($"Ordinary price={price1}");
                    return price1;
                }
    
                public double Favourable(double price)
                {
                    double price1 = 0.85 * price;
                    Console.WriteLine($"Favourable price={price1}");
                    return price1;
                }
            }
    
            static void Main(string[] args)
            {
                #region 多路广播委托
                Example example = new Example();
                MyDelegateDouble myDelegateDouble = new MyDelegateDouble(example.Ordinary);
                myDelegateDouble += new MyDelegateDouble(example.Favourable);
                Console.WriteLine($"Current Price={myDelegateDouble(100)}");
                Console.Read();
                #endregion
            }
        }
    
      10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    运行结果如下:

    在这里插入图片描述

    3.4 浅谈Observer模式(观察者模式)

    简单回顾一下Observer模式,它使用一对多的方式,可以让多个观察者同时关注同一个事物,并作出不同的响应。

    如下例,Manager的底薪为基本工资的1.5倍,Assistant的底薪为基本工资的1.2倍。WageManager类的RegisterWorker方法与RemoveWorker方法可以

    用于注册和注销观察者,最后执行Execute方法可以对多个已注册的观察者同时输入参数。

    下面代码演示使用非委托方式实现观察者模式:

        class Program
        {
            #region 非委托观察者模式
            /// <summary>
            /// 工作者类
            /// </summary>
            public abstract class Worker
            {
                public abstract double GetWages(double basicWages);
            }
    
            /// <summary>
            /// 管理级类
            /// </summary>
            public class Manager : Worker
            {
                public override double GetWages(double basicWages)
                {
                    double totalWages = 1.5 * basicWages;
                    Console.WriteLine($"Manager's wages is:{totalWages}");
                    return totalWages;
                }
            }
    
            /// <summary>
            /// 助理级类
            /// </summary>
            public class Assistant : Worker
            {
                public override double GetWages(double basicWages)
                {
                    double totalWages = 1.2 * basicWages;
                    Console.WriteLine($"Assistant's wages is:{totalWages}");
                    return totalWages;
                }
            }
    
            /// <summary>
            /// 工资管理类
            /// </summary>
            public class WageManager
            {
                IList<Worker> workerList = new List<Worker>();
    
                public void RegisterWorker(Worker worker)
                {
                    workerList.Add(worker);
                }
    
                public void RemoveWorker(Worker worker)
                {
                    workerList.Remove(worker);
                }
    
                public void Excute(double basicWages)
                {
                    if (workerList.Count != 0)
                    {
                        foreach (var worker in workerList)
                        {
                            worker.GetWages(basicWages);
                        }
                    }
                }
            }
            #endregion
    
            static void Main(string[] args)
            {
                #region 非委托观察者模式
                WageManager wageManager = new WageManager();
                //注册观察者
                wageManager.RegisterWorker(new Manager());
                wageManager.RegisterWorker(new Assistant());
                //同时输入底薪3000元,分别进行计算。
                wageManager.Excute(3000);
                Console.Read();
                #endregion
            }
        }
    
      10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    运行结果如下:
    
    • 1

    在这里插入图片描述

    开发Observer模式时若借助委托,可以进一步简化开发过程。由于委托对象支持多路广播,所以可以把Worker类省略。在WageManager类中建立了一个

    委托对象wageHandler,通过Attach与Detach方法可以分别加入及取消委托。如果观察者想对事物进行监测,只需要加入一个委托对象即可。在第二节提过,

    委托的GetInvodationList方法能获取多路广播委托列表,在Execute方法中,就是通过多路广播委托列表去判断所绑定的委托数量是否为0。

    下面代码演示使用委托方式实现观察者模式:

     class Program
        {
            #region 委托观察者模式
            public delegate double Handler(double basicWages);
    
            public class Manager
            {
                public double GetWages(double basicWages)
                {
                    double totalWages = 1.5 * basicWages;
                    Console.WriteLine($"Manager's wages is:{totalWages}");
                    return totalWages;
                }
            }
    
            public class Assistant
            {
                public double GetWages(double basicWages)
                {
                    double totalWages = 1.2 * basicWages;
                    Console.WriteLine($"Assistant's wages is:{totalWages}");
                    return totalWages;
                }
            }
    
            public class WageManager
            {
                private Handler wageHandler;
    
                //加入观察者
                public void Attach(Handler wageHandler1)
                {
                    wageHandler += wageHandler1;
                }
    
                //删除观察者
                public void Detach(Handler wageHandler1)
                {
                    wageHandler -= wageHandler1;
                }
    
                //通过GetInvodationList方法获取多路广播委托列表,如果观察者数量大于0即执行方法。
                public void Execute(double basicWages)
                {
                    if (wageHandler != null)
                    {
                        if (wageHandler.GetInvocationList().Count() != 0)
                        {
                            wageHandler(basicWages);
                        }
                    }
                }
            }
            #endregion
    
            static void Main(string[] args)
            {
                #region 委托观察者模式
                WageManager wageManager = new WageManager();
                //加入Manager观察者
                Manager manager = new Manager();
                Handler managerHandler = new Handler(manager.GetWages);
                wageManager.Attach(managerHandler);
    
                //加入Assistant观察者
                Assistant assistant = new Assistant();
                Handler assistantHandler = new Handler(assistant.GetWages);
                wageManager.Attach(assistantHandler);
    
                //同时加入底薪3000元,分别进行计算
                wageManager.Execute(3000);
                Console.ReadKey();
                #endregion
            }
        }
    
      10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75

    运行结果如下:

    在这里插入图片描述

    3.5 委托的协变与逆变

    在Framework 2.0出现之前,委托协变这个概念还没有出现。此时因为委托是安全类型,它们不遵守继承的基础规则。即会这下面的情况:Manager虽然

    是Worker的子类,但GetWorkerHander委托不能直接绑定GetManager方法,因为在委托当中它们的返回值Manager与Worker被视为完全无关的两个类型。

    自Framework 2.0面世以后,委托协变的概念就应运而生,此时委托可以按照传统的继承规则进行转换。即GetWorkerHandler委托可以直接绑定

    GetManager方法。

    下面代码演示委托的协变:

        class Program
        {
            #region 委托的协变
            /// <summary>
            /// 在Framework 2.0以上可绑定GetWorker与GetManager两个方法
            /// </summary>
            /// <param name="id"></param>
            /// <returns></returns>
            public delegate Worker GetWorkerHandler(int id);
    
            public class Worker
            {
                public Worker() { }
    
                public Worker(int id)
                {
                    Id = id;
                }
    
                public int Id { get; set; }
    
                public void ShowId()
                {
                    Console.WriteLine($"Id={Id}");
                }
            }
    
            public class Manager : Worker
            {
                public Manager() { }
                public Manager(int id)
                {
                    Id = id;
                }
            }
    
            public static Worker GetWorker(int id)
            {
                Worker worker = new Worker(id);
                return worker;
            }
    
            public static Manager GetManager(int id)
            {
                Manager manager = new Manager(id);
                return manager;
            }
            #endregion
    
            static void Main(string[] args)
            {
                #region 委托的协变
                GetWorkerHandler workerHandler = new GetWorkerHandler(GetWorker);
                Worker worker = workerHandler(1);
                worker.ShowId();
                GetWorkerHandler managerHandler = new GetWorkerHandler(GetManager);
                Manager manager = managerHandler(2) as Manager;
                manager.ShowId();
                Console.Read();
                #endregion
            }
        }
    
      10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    运行结果如下:
    
    • 1

    在这里插入图片描述

    委托逆变,是指委托方法的参数同样可以接收 “继承” 这个传统规则。像下面的例子,以object为参数的委托,可以接受任何object子类的对象作为参数。

    最后可以在处理方法中使用is对输入数据的类型进行判断,分别处理对不同的类型的对象。

    下面代码演示委托的逆变:

        class Program
        {
            #region 委托的逆变
            public delegate void Handler(object obj);
    
            public static void GetMessage(object message)
            {
                if (message is string)
                    Console.WriteLine("His name is:" + message.ToString());
                if (message is int)
                    Console.WriteLine("His age is:" + message.ToString());
            }
            #endregion
    
            static void Main(string[] args)
            {
                #region 委托的逆变
                Handler handler = new Handler(GetMessage);
                handler(29);
                Console.Read();
                #endregion
            }
        }
    
      10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    运行结果如下:

    在这里插入图片描述

    注:委托与其绑定方法的参数必须一至,即当 Handler 所输入的参数为 A 类型,其绑定方法 GetMessage 的参数也必须为 A 类或者 A 的父类 。相反,

    当绑定方法的参数为 A 的子类,系统也无法辨认。

    3.6 泛型委托

    委托逆变虽然实用,但如果都以object作为参数,则需要每次都对参数进行类型的判断,这不禁令人感到厌烦。

    为此,泛型委托应运而生,泛型委托有着委托逆变的优点,同时利用泛型的特性,可以使一个委托绑定多个不同类型参数的方法,而且在方法中不需要
    
    • 1

    使用is进行类型判断,从而简化了代码。

    下面代码演示泛型委托:

      class Program
        {
            #region 泛型委托
            public delegate void Handler<T>(T obj);
    
            /// <summary>
            /// 工作者类
            /// </summary>
            public class Worker
            {
                public double Wages { get; set; }
            }
    
            /// <summary>
            /// 管理级类
            /// </summary>
            public class Manager : Worker
            {
            }
    
            public static void GetWorkerWages(Worker worker)
            {
                Console.WriteLine("Worker's total wages is:" + worker.Wages);
            }
    
            public static void GetManagerWages(Manager manager)
            {
                Console.WriteLine("Manager's total wages is:" + manager.Wages);
            }
            #endregion
    
            static void Main(string[] args)
            {
                #region 泛型委托
                Handler<Worker> workerHander = new Handler<Worker>(GetWorkerWages);
                Worker worker = new Worker
                {
                    Wages = 3000
                };
                workerHander(worker);
    
                Handler<Manager> managerHandler = new Handler<Manager>(GetManagerWages);
                Manager manager = new Manager
                {
                    Wages = 4500
                };
                managerHandler(manager);
    
                Console.ReadKey();
                #endregion
            }
        }
    
      10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    运行结果如下:
    
    • 1

    在这里插入图片描述

    四、深入解析事件

    4.1 事件的由来

    在介绍事件之前大家可以先看看下面的例子,PriceManager负责对商品价格进行处理,当委托对象GetPriceHandler的返回值大于100元,按8.8折计算,

    低于100元按原价计算。

      class Program
        {
            #region 事件的由来
            public delegate double PriceHandler();
            public class PriceManager
            {
                public PriceHandler GetPriceHandler;
    
                //委托处理,当价格高于100元按8.8折计算,其他按原价计算。
                public double GetPrice()
                {
                    if (GetPriceHandler.GetInvocationList().Count() > 0)
                    {
                        if (GetPriceHandler() > 100)
                            return GetPriceHandler() * 0.88;
                        else
                            return GetPriceHandler();
                    }
                    return -1;
                }
            }
            //书本价格为98元
            public static double BookPrice()
            {
                return 98.0;
            }
            //计算机价格为8800元
            public static double ComputerPrice()
            {
                return 8800.0;
            }
            #endregion
    
            static void Main(string[] args)
            {
                #region 事件的由来
                PriceManager priceManager = new PriceManager
                {
                    //调用priceManager的GetPrice方法获取价格
                    //直接调用委托的Invoke获取价格,两者进行比较。
                    GetPriceHandler = new PriceHandler(ComputerPrice)
                };
                Console.WriteLine(string.Format("GetPrice\n Computer's price is {0}",priceManager.GetPrice()));
                Console.WriteLine(string.Format("Invoke\n Computer's price is {0}",priceManager.GetPriceHandler.Invoke()));
                Console.WriteLine();
                priceManager.GetPriceHandler = new PriceHandler(BookPrice);
                Console.WriteLine(string.Format("GetPrice\n Book's price is {0}",priceManager.GetPrice()));
                Console.WriteLine(string.Format("Invoke\n Book's price is {0}",priceManager.GetPriceHandler.Invoke()));
                Console.Read();
                #endregion
            }
        }
    
    
      10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    运行结果如下:
    
    • 1

    在这里插入图片描述

    观察运行的结果,如果把委托对象GetPriceHandler设置为public,外界可以直接调用GetPriceHandler.Invoke获取运行结果而移除了GetPrice方法的处理,

    这正是开发人员最不想看到的。

    为了保证系统的封装性,开发往往需要把委托对象GetPriceHandler设置为private,再分别加入AddHandler、RemoveHandler方法对GetPriceHandler委托

    对象进行封装。为了保存封装性,很多操作都需要加入AddHandler、RemoveHandler这些相似的方法代码,这未免令人感到厌烦。

    为了进一步简化操作,事件这个概念应运而生。
    4.2 事件的定义

    事件(event)可被视作为一种特别的委托,它为委托对象隐式地建立起add_XXX、remove_XXX两个方法,用作注册与注销事件的处理方法,而且事件对

    应的变量成员将会被视为private变量,外界无法超越事件所在对象直接访问它们,这使事件具备良好的封装性,而且免除了add_XXX、remove_XXX等繁琐

    的代码。

    #region 事件的定义
    
    
    public class EventTest
    {
        public delegate void MyDelegate();
        public event MyDelegate MyEvent;
    }
    #endregion
    

      使用ILDASM.exe观察事件成员,系统为MyEvent事件自动建立add_MyEvent、remove_MyEvent 方法。

      4.3 事件的使用方式

      事件能通过+=和-=两个方式注册及注销对其处理的方法,使用+=与-=操作符的时候,系统会自动调用对应的add_XXX、remove_XXX进行处理。

      值得留意,在PersonManager类的Execute方法中,如果MyEvent绑定的处理方法不为空,即可使用MyEvent(string)引发事件。但如果在外界的Main方法中

      直接使用personManager.MyEvent(string)来引发事件,系统将引发错误报告。这正是因为事件具备了良好的封装性,使外界不能超越事件所在的对象访问其变

      量成员。

      注:在事件所处的对象之外,事件只能出现在+=、-=的左方。

      下面代码演示事件的使用:

        class Program
          {
              #region 事件的使用
              public delegate void MyDelegate(string name);
      
              public class PersonManager
              {
                  public event MyDelegate MyEvent;
      
                  //执行事件
                  public void Execute(string name)
                  {
                      if (MyEvent != null)
                      {
                          MyEvent(name);
                      }
                  }
              }
      
              public static void GetName(string name)
              {
                  Console.WriteLine("My name is " + name);
              }
              #endregion
      
              static void Main(string[] args)
              {
                  #region 事件的使用
                  PersonManager personManager = new PersonManager();
                  //绑定事件处理方法
                  personManager.MyEvent += new MyDelegate(GetName);
                  personManager.Execute("Atomy");
                  Console.Read();
                  #endregion
              }
          }
      
        10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      运行结果如下:
      
      • 1

      在这里插入图片描述

      4.4 事件处理方法的绑定
      在绑定事件处理方法的时候,事件出现在+=、-= 操作符的左边,对应的委托对象出现在+=、-= 操作符的右边。对应以上例子,事件提供了更简单的绑定方式,

      只需要在+=、-= 操作符的右方写上方法名称,系统就能自动辩认。

      下面代码演示事件处理方法的绑定:

         class Program
          {
              #region 事件的使用及方法绑定
              public delegate void MyDelegate(string name);
      
              public class PersonManager
              {
                  public event MyDelegate MyEvent;
      
                  //执行事件
                  public void Execute(string name)
                  {
                      if (MyEvent != null)
                      {
                          MyEvent(name);
                      }
                  }
              }
      
              public static void GetName(string name)
              {
                  Console.WriteLine("My name is " + name);
              }
              #endregion
      
              static void Main(string[] args)
              {
                  #region 事件的使用及方法绑定
                  PersonManager personManager = new PersonManager();
                  //绑定事件处理方法方式一
                  personManager.MyEvent += new MyDelegate(GetName);
                  //绑定事件处理方法方式二
                  personManager.MyEvent += GetName;
                  personManager.Execute("Atomy");
                  Console.Read();
                  #endregion
              }
          }
      
        10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      运行结果如下:
      
      • 1

      在这里插入图片描述

      如果觉得编写GetName方法过于麻烦,还可以使用匿名方法绑定事件的处理。

      下面代码演示事件处理方法的匿名方法绑定:

         class Program
          {
              #region 事件的使用
              public delegate void MyDelegate(string name);
      
              public class PersonManager
              {
                  public event MyDelegate MyEvent;
      
                  //执行事件
                  public void Execute(string name)
                  {
                      if (MyEvent != null)
                      {
                          MyEvent(name);
                      }
                  }
              }
      
              public static void GetName(string name)
              {
                  Console.WriteLine("My name is " + name);
              }
              #endregion
      
              static void Main(string[] args)
              {
                  #region 事件的使用及方法绑定
                  PersonManager personManager = new PersonManager();
                  //绑定事件处理方法方式一
                  personManager.MyEvent += new MyDelegate(GetName);
                  //绑定事件处理方法方式二
                  personManager.MyEvent += GetName;
                  //绑定事件处理方法方式三(匿名方法)
                  personManager.MyEvent += delegate (string name) { Console.WriteLine("My name is " + name); };
                  personManager.Execute("Atomy");
                  Console.Read();
                  #endregion
              }
          }
      
        10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40

      运行结果如下:
      在这里插入图片描述

      4.5 C#控件中的事件

      在C#控件中存在很多的事件,比如Click、TextChanged、SelectIndexChanged等等,很多都是通过EventHandler委托绑定事件的处理方式,EventHandler

      可说是C#控件中最常见的委托 。

      public delegate void EventHandler (Object sender, EventArgs e)

      EventHandler委托并无返回值,sender代表引发事件的控件对象,e代表由该事件生成的数据 。

      下面代码演示C#控件中的事件绑定:

        public partial class EventTest : Form
          {
              public EventTest()
              {
                  InitializeComponent();
              }
      
              private void EventTest_Load(object sender, EventArgs e)
              {
                  btnEvent.Click += new EventHandler(btnEvent_onclick);
              }
      
              public void btnEvent_onclick(object sender,EventArgs e)
              {
                  Button button = (Button)sender;
                  MessageBox.Show(button.Text);
              }
          }
      
        10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      运行结果如下:
      
      • 1

      在这里插入图片描述

      EventHandler只是EventHandler泛型委托的一个简单例子。事实上,大家可以利用 EventHandler 构造出所需要的委托。

      public delegate void EventHandler<TEventArgs> (Object sender, TEventArgs e)
      
      • 1

      在EventHandler中,sender代表事件源,e代表派生自EventArgs类的事件参数。开发人员可以建立派生自EventArgs的类,从中加入需要使用
      到的事件参数,然后建立EventHandler委托。

      下面的例子中,先建立一个派生自EventArgs的类MyEventArgs作为事件参数,然后在EventManager中建立事件myEvent , 通过Execute方法可以激发事件。

      最后在测试中绑定myEvent的处理方法ShowMessage,在ShowMessage显示myEventArgs的事件参数Message。

        class Program
          {
              #region EventArgs派生
              public class MyEventArgs : EventArgs
              {
                  private string args;
      
                  public MyEventArgs(string message)
                  {
                      args = message;
                  }
      
                  public string Message
                  {
                      get { return args; }
                      set { args = value; }
                  }
              }
      
              public class EventManager
              {
                  public event EventHandler<MyEventArgs> myEvent;
      
                  public void Execute(string message)
                  {
                      myEvent?.Invoke(this, new MyEventArgs(message));
                  }
              }
      
              public static void ShowMessage(object obj, MyEventArgs e)
              {
                  Console.WriteLine(e.Message);
              }
              #endregion
      
              static void Main(string[] args)
              {
                  #region EventArgs派生
                  EventManager eventManager = new EventManager();
                  eventManager.myEvent += new EventHandler<MyEventArgs>(ShowMessage);
                  eventManager.Execute("How are you?");
                  Console.Read();
                  #endregion
              }
          }
      
        10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45

      运行结果如下:
      在这里插入图片描述

      4.6 为用户控件建立事件

      开发过程中,往往会出现很多类似的控件与代码,开发人员可以通过用户控件来避免重复的代码。但往往同一个用户控件,在不同的页面中需要有不同的响应。

      此时为用户控件建立事件,便可轻松地解决此问题。

      新建一个Person类:

          class Person
          {
              public int Id { get; set; }
              public string Name { get; set; }
              public int Age { get; set; }
          }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6

      新建一个用户控件,拖入一个DataGridView,命名为:DataGridViewControl。

        public partial class DataGridViewControl : UserControl
          {
              public DataGridViewControl()
              {
                  InitializeComponent();
              }
      
              private void DataGridViewEvent_Load(object sender, EventArgs e)
              {
                  dataGridView1.DataSource = GetPersonList();
              }
      
              //数据源
              private IList<Person> GetPersonList()
              {
                  IList<Person> list = new List<Person>();
                  Person person = new Person
                  {
                      Id = 1,
                      Name = "Hello",
                      Age = 1
                  };
                  list.Add(person);
                  person = new Person
                  {
                      Id = 2,
                      Name = "World",
                      Age = 2
                  };
                  list.Add(person);
                  return list;
              }
      
              //单元格事件
              public event DataGridViewCellEventHandler CellEventHandler;
      
              private void dataGridView1_RowEnter(object sender, DataGridViewCellEventArgs e)
              {
                  CellEventHandler?.Invoke(sender, e);
              }
          }
      
        10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41

      新建一个WinForm窗体,命名为Main,并拖入DataGridViewControl用户控件。

       public partial class Main : Form
          {
              public Main()
              {
                  InitializeComponent();
              }
      
              private void Main_Load(object sender, EventArgs e)
              {
                  dataGridViewControl1.CellEventHandler += DataGridViewControl1_CellEventHandler;
              }
      
              private void DataGridViewControl1_CellEventHandler(object sender, DataGridViewCellEventArgs e)
              {
                  DataGridView dataGridView1 = (DataGridView)sender;
                  int rowIndex = int.Parse(e.RowIndex.ToString());
                  int columnIndex = int.Parse(e.ColumnIndex.ToString());
                  MessageBox.Show(dataGridView1.Rows[rowIndex].Cells[columnIndex].Value.ToString());
              }
          }
      
        10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      运行结果如下:
      
      • 1

      在这里插入图片描述

      使用控件已有的事件固然简单,但它限制了传送的参数类型,使开发人员无法传送额外的自定义参数。在结构比较复杂的用户控件中,使用已有的控件事件,

      显然不够方便,此时,您可以考虑为用户控件建立自定义事件。

      新建一个MyEventArgs类:

        public class MyEventArgs : EventArgs
          {
              public string Name { get; set; }                    //买家姓名
              public string Address { get; set; }                 //买家地址
              public string Tel { get; set; }                     //买家电话
              public string OrderCode { get; set; }               //订单号码
              public IList<OrderItem> OrderItemList { get; set; } //订单明细
              public MyEventArgs(string name, string address, string tel, string orderCode, IList<OrderItem> orderItemList)
              {
                  Name = name;
                  Address = address;
                  Tel = tel;
                  OrderCode = orderCode;
                  OrderItemList = orderItemList;
              }
          }
      
        10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16

      新建一个OrderItem类:

       public class OrderItem
          {
              public string OrderItemID { get; set; } //明细单ID
              public string Goods { get; set; }       //商品名称
              public double Price { get; set; }       //商品单价
              public int Count { get; set; }          //商品数量
      
              public OrderItem(string id, string goods, double price, int count)
              {
                  OrderItemID = id;
                  Goods = goods;
                  Price = price;
                  Count = count;
              }
          }
      
        10
      • 11
      • 12
      • 13
      • 14
      • 15

      新建一个用户控件,拖入一个DataGridView及一个Button,命名为:OrderControl。

          public partial class OrderControl : UserControl
          {
              //自定义委托及事件  
              public delegate void MyDelegate(object sender, MyEventArgs myEventArgs);
              public event MyDelegate MyEvent;
      
              public OrderControl()
              {
                  InitializeComponent();
              }
      
              private void OrderControl_Load(object sender, EventArgs e)
              {
                  dataGridView1.DataSource = GetList();
              }
      
              //数据源
              private IList<OrderItem> GetList()
              {
                  IList<OrderItem> list = new List<OrderItem>();
                  OrderItem orderItem = new OrderItem("1", "鼠标", 160, 2);
                  list.Add(orderItem);
                  orderItem = new OrderItem("https://cdn.jxasp.com:9143/image/2", "键盘", 80, 2);
                  list.Add(orderItem);
                  return list;
              }
      
              private void button1_Click(object sender, EventArgs e)
              {
                  if (MyEvent != null)
                  {
                      if (dataGridView1.CurrentCell != null)
                      {
                          int rowIndex = int.Parse(dataGridView1.CurrentCell.RowIndex.ToString());
                          MyEventArgs myEventArgs = new MyEventArgs
                          (
                              "张三",
                              "广东",
                              "111111111111",
                              "https://cdn.jxasp.com:9143/image/2019122300001",
                              GetList()
                          );
                          MyEvent(this, myEventArgs);
                      }
                  }
              }
          }
      
        10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47

      新建一个WinForm窗体,命名为OrderForm,并拖入OrderControl用户控件。

         public partial class OrderForm : Form
          {
              public OrderForm()
              {
                  InitializeComponent();
              }
      
              private void OrderForm_Load(object sender, EventArgs e)
              {
                  orderControl1.MyEvent += OrderControl1_MyEvent;
              }
      
              private void OrderControl1_MyEvent(object sender, MyEventArgs myEventArgs)
              {
                  MessageBox.Show($"Name={myEventArgs.Name},Address={myEventArgs.Address},Tel={myEventArgs.Tel}","信息",MessageBoxButtons.OK,MessageBoxIcon.Information); 
              }
          }
      
        10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17

      运行结果如下:

      在这里插入图片描述

      相关技术文章

      点击QQ咨询
      开通会员
      返回顶部
      ×
      微信扫码支付
      微信扫码支付
      确定支付下载
      请使用微信描二维码支付
      ×

      提示信息

      ×

      选择支付方式

      • 微信支付
      • 支付宝付款
      确定支付下载