委托基础详解

/*委托是一个类型安全的对象
        * 它指向程序中另一个以后被调用的方法或多个方法。委托类型包含3个重要的信息:
        * 1:它所调用的方法的名称
        * 2:该方法的参数(可选)
        * 3:该方法的返回值(可选)
        * 注:.net委托既可以指向静态方法,也可以指向实例方法
        *
        * 当一个委托对象被创建并提供了上述信息后,它可以在运行时动态调用其他指向的方法,可以看到 .Net Framework 中每个委托(包含自定义委托)
        * 都被自动赋予同步或异步访问方法的能力,可以不用手工创建与管理一个Thread对象而直接调用另一个辅助线程上的方法。大大简化了编程工作
     */

一个简单的委托事例

委托的定义用delegate关键字

//这个委托可以指向任何传入两个整数返回一个整数的方法
        public delegate int BinaryOp(int x, int y);

定义一个类

 

  public class SimpleMath
    {
        //这个类包含了BinaryOp将指向的方法
        public static int Add(int x, int y)
        {
            return x + y;
        }
    }

调用

      #region 一个简单的委托事例
            /*一个简单的委托事例*/

            //创建一个指向SimpleMath.Add()方法的BinaryOp对象
            BinaryOp b = new BinaryOp(SimpleMath.Add);

            /*使用委托对象间接调用Add()方法  底层:Invoke()在这里被调用了,可以:b.Invoke(10,10),不过我们不需要显示的调用Invoke();
             * 在底层,运行库实际上在MulticastDelegate派生类上调用了编译器生成的Invoke()方法
             */
            Console.WriteLine("10+10 is {0}", b(10, 10));  //20
            #endregion

/*当C#编译处理委托类型时,它先自动生成一个派生自 System.MulticastDelegate的密封类(BinaryOp)如图
             * 这个类与它的基类System.Delegate一起为委托提供必要的基础设施,以维护(委托指向的方法)
             * 以后将要调用方法的列表。
             * 我们生成项目后。把.exe文件拖到Reflector中
             *
             *
             * 可以看到生成后,Program类定义了3个公共方法
             * 1:Invoke()是核心方法,因为他被用来以同步方式调用委托对象维护(委托指向的方法)的每个方法
             * 这里的同步就是指被调用者必须等待调用完成才 能继续执行
             * 2:BeginInvoke()和EndInvoke()方法能在第二个执行线程上异步调用当前方法
             *
             * 那么编译器又是如何确切知道怎样定义Invoke()、BeginInvoke()和EndInvoke()方法的呢?
             * 把图中的BinaryOp分解如下:sealed代表密封
             * sealed class BinaryOp:System.MulticastDelegate
             * {
             *      public int Invoke(int x,int y);
             *      public IAsyncResult BeginInvoke(int x,int y,AsyncCallback callback,object state);
             *      public int EndInvoke(IAsyncResult result);
             * }
             * 解:
             * Invoke()方法定义的参数和返回值完全匹配 BinaryOp 委托的定义。
             * BeginInvoke()方法的参数(这里是两个整数)也基于 BinaryOp 委托,但BeginInvoke总是提供最后两个参数(AsyncCallback类型与object类型)用于
             * 异步方法调用。
             * 最后EndInvoke()方法的返回值与初始化委托 BinaryOp 声明相同,总是以一个实现了IAsyncResult接口的对象作为唯一的参数。
             *
             *
             * 同理:public delegate string MyDelegate(bool a,bool b,bool c)
             * 那么:
             * sealed class MyDelegate:System.MulticastDelegate
             * {
             *      public string Invoke(bool a,bool b,bool c);
             *      public IAsyncResult BeginInvoke(bool a,bool b,bool c,AsyncCallback callback,object state);
             *      public string EndInvoke(IAsyncResult result);
             * }
             *
             * 委托还可以指向包含任意数量out 或 ref参数(已经params关键字标记的数组参数)的方法
             *
             * 同理:public delegate string MyOtherDelegate(out bool a,ref bool b,int c)
             * 那么:Invoke()和BeginInvoke()方法都没变。但EndInvoke()方法有所变化,其中包括委托类型定义的所有out/ref参数
             * sealed class MyOtherDelegate:System.MulticastDelegate
             * {
             *      public string Invoke(out bool a,ref bool b,int c);
             *      public IAsyncResult BeginInvoke(out bool a,ref bool b,int c,AsyncCallback callback,object state);
             *      public string EndInvoke(out bool a,ref bool b,IAsyncResult result);
             * }
             *
             * 故:C#委托类型的定义会生成一个密封类,它包含3个编译器生成的方法,这3个方法的参数与返回值基于委托的声明。
             *
             * System.MulticastDelegate和System.Delegate基类
             *
             * 使用C#中delegate关键字创建委托的时候,也间接的声明了一个派生自System.MulticastDelegate的类,这个类使其继承类可以访问包含由委托对象
             * 维护的方法地址的列表已经一些处理调用列表的附加方法(与少数重载的操作符)
             *
             *
             *
            */

 

使用委托发送对象状态通知

  /*
        *1:定义将通知发送给调用者的委托类型
        * 2:声明Car类中每个委托类型的成员变量
        * 3:在Car上创建辅助函数使调用者能指定由委托成员变量保存的方法
        * 4:修改Accelerate()方法在适当的情形下调用委托的调用列表
    */

  class Car
    {

        //1:定义委托类型
        public delegate void CarEngineHandler(string msgForCaller);

        //2:定义每个委托类型的成员变量
        private CarEngineHandler listOfHandlers;

        //3:向调用者添加注册函数,自定义的注册方法封装的私有委托成员变量
        public void RegisterWithCarEngine(CarEngineHandler methodToCall)
        {
            listOfHandlers = methodToCall;

            #region 多路广播
            //委托支持多路广播,即一个委托对象可以维护一个可调用方法的列表而不只是单独一个方法,给一个委托对象添加多个方法时,不用直接分配(赋值操作符 = ),重载+=操作符即可
            //listOfHandlers += methodToCall; (底层是使用的Delegate.Combine()方法)
            //注:既然+=操作符 是添加方法 那么-=操作符则是从委托调用列表中移除成员 (底层是使用的Delegate.Remove()方法)
            #endregion
        }

        /*
           注:将委托定义在使用它的类型作用域里是很普遍的
        * 严格来说,可以将委托成员变量定义为公共的,这样就不需要创建额外的注册方法,然而,如果将委托成员定义为私有的。我们就强制了封装服务并提供了更加类型安全的解决方案。
         * 比如:C# event关键字,声明为事件
        */

        //内部状态数据
        public int CurrentSpeed { get; set; } //当前速度
        public int MaxSpeed { get; set; } //最大速度
        public string PetName { get; set; } //小汽车名字

        //汽车能用还是不能用
        private bool carIsDead;

        //类的构造函数
        public Car() { MaxSpeed = 100; }
        public Car(string name, int maxSp, int currSp)
        {
            CurrentSpeed = currSp;
            MaxSpeed = maxSp;
            PetName = name;
        }

        //此方法:使Car对象向订阅者发送引擎相关的信息。
        public void Accelerate(int delta)
        {
            //当前汽车是否不能用
            if (carIsDead)
            {
                //判断当前是否有订阅者
                if (listOfHandlers != null)
                {
                    listOfHandlers("您的汽车已经报废");
                }
            }
            else
            {
                CurrentSpeed += delta;

                //假设。距离最大速度剩下10 则发送警报信息
                if (10 == (MaxSpeed - CurrentSpeed) && listOfHandlers != null)
                {
                    listOfHandlers("不能在加速了,滴滴哒哒");
                }
                if (CurrentSpeed >= MaxSpeed) carIsDead = true;
                else Console.WriteLine("当前速度 = {0}", CurrentSpeed);
            }
        }
    }

测试代码

  

//使用委托发送对象状态通知
            #region 使用委托发送对象状态通知
            /*
            Console.WriteLine("小汽车上路。。。。\n");

            //首先:创建一个Car对象
            Car c1 = new Car("BMW", 100, 10);

            //现在:告诉汽车,它想要想我们发送信息时调用哪个方法
            c1.RegisterWithCarEngine(new Car.CarEngineHandler(OnCarEngineEvent));

            //更简单的写法(方法组转换语法),即 在调用以委托作为参数的方法时,直接提供方法的名称,而不用创建委托对象
            c1.RegisterWithCarEngine(OnCarEngineEvent);

            //然后:加速,将会触发事件
            Console.WriteLine("**加速***");
            for (int i = 0; i < 6; i++)
            {
                c1.Accelerate(20);
            }
            */
            #endregion

要传入事件的函数

  //要传入的事件方法
        public static void OnCarEngineEvent(string msg)
        {
            Console.WriteLine("\n***报警信息*****");
            Console.WriteLine("信息提示 => {0}", msg);
            Console.WriteLine("\n********");
        }

委托协变

//委托协变,你有没有想过,如果委托类型指向返回自定义类类型的方法?
            /*
             * 说白了协变和逆变也就是,创建一个委托,指向多个方法,方法的参数是存在继承关系的对象,之间的一个转换
             
             */

定义两个类。必须有继承关系

  class China : Person
    {
        public void show()
        {
            if (tag == null) Console.WriteLine("派生类没有注册");
            else Console.WriteLine("派生类已经注册");
        }
    }

    class Person
    {
        public delegate Person DelegateHander();
        protected DelegateHander tag;

        public void Register(DelegateHander meth)
        {
            tag = meth;
        }
        public void show()
        {
            if (tag == null) Console.WriteLine("父类没有注册");
            else Console.WriteLine("父类已经注册");
        }
    }

定义与委托签名匹配的函数

public static Person GetPer()
        {
            return new Person();
        }
        public static China GetChi()
        {
            return new China();
        }

测试代码

#region 委托协变
            //委托协变,你有没有想过,如果委托类型指向返回自定义类类型的方法?
            /*
             * 说白了协变和逆变也就是,创建一个委托,指向多个方法,方法的参数是存在继承关系的对象,之间的一个转换

             */
            /*Person tag = new Person();
            tag.Register(GetPer);

            Person p = tag;
            tag.show();

            Person ct = new China();
            //协变允许这种目标对象赋值,
            ct.Register(GetChi);
            //委托本来是返回Person类型的。但因为协变,做了一个显示强制类型转换,这里得到派生类 类型的方法,
            China c = (China)ct;
            c.show();
             */

            #endregion

泛型委托

 如果你希望定义一个委托类型来调用任何返回void并且接受单个参数的方法,但这个参数类型可能会不同,那么,就可以通过类型参数来构建 泛型委托

泛型委托的定义:

 

 //这个泛型委托可以调用任何返回void并接受单个参数的方法
        public delegate void myGenericDelegate<T>(T tag);

myGenericDelegate<T>定义了一个类型参数表示要传入委托目标的实参,在创建类型实例时,需要指定类型参数的值以及委托调用的方法的名称。如:
            //如果使用字符串作为类型参数
            //myGenericDelegate<string> strTarget = new myGenericDelegate<string>(StringTarget);
            //strTarget("字符串类型参数");

static void StringTarget(string tag)
        {
            Console.WriteLine("传来的字符串是 {0}", tag);
        }

   #region 泛型委托
            //泛型委托
            /*
                疑问:如果你希望定义一个委托类型来调用任何返回void并且接受单个参数的方法,但这个参数类型可能会不同,那么,就可以通过类型参数来构建 泛型委托
             *
             * myGenericDelegate<T>定义了一个类型参数表示要传入委托目标的实参,在创建类型实例时,需要指定类型参数的值以及委托调用的方法的名称。如:
             */
            //如果使用字符串作为类型参数
            //myGenericDelegate<string> strTarget = new myGenericDelegate<string>(StringTarget);
            //strTarget("字符串类型参数");
            #endregion

C# 事件

      //C# 事件
            /*
                从上面使用的委托来看,使用委托会有一些重复的代码,当定义委托,需要声明必要的成员变量已经创建自定义的注册/注销方法来保护封装。
             * 如果没有把委托定义为私有的,那么调用者就可以直接访问委托对象,这样调用者就可以把变量重新赋值为新的委托对象,实际上也就删除了当前要调用的方法列表。
             * 也就是说,调用者可以直接调用委托列表
             */

定义一个Animal类

 

   class Animal
    {
        /*
            定义一个事件分为两个步骤:
         * 1:需要定义一个委托类型,它包含在事件触发时将要调用的方法
         * 2:通过Event关键字用相关委托声明这个事件
         */

        //这个委托用来与Animal的事件协作
        public delegate void AnimalEngineHandler(string name);//声明事件
        public event AnimalEngineHandler Call;

        //提示方法
        public void showMsg(string name)
        {
            if (Call != null)
            {
                if (name == "熊猫") Call("杀了它你就是国宝!");
                else Call("^-^");
            }
        }

    }

编写测试代码

 #region C# 事件
            //C# 事件
            /*
                从上面使用的委托来看,使用委托会有一些重复的代码,当定义委托,需要声明必要的成员变量已经创建自定义的注册/注销方法来保护封装。
             * 如果没有把委托定义为私有的,那么调用者就可以直接访问委托对象,这样调用者就可以把变量重新赋值为新的委托对象,实际上也就删除了当前要调用的方法列表。
             * 也就是说,调用者可以直接调用委托列表
             */
            //传入监听的事件,调用者只需要使用+=和-=操作符即可
            //Animal.AnimalEngineHandler c = new Animal.AnimalEngineHandler(AnimalMsg);
            //Animal a = new Animal();
            //a.Call += c;
            //a.showMsg("dd");

            Animal an = new Animal();
            //an.Call += new Animal.AnimalEngineHandler(AnimalMsg);
            //更简单的方法,利用方法组转换
            //an.Call += AnimalMsg;
            //an.showMsg("熊猫");
            #endregion

 static void AnimalMsg(string msg)        {            Console.WriteLine(msg);        }

创建自定义的事件参数

            /*
                微软推荐的事件模式:第一个参数是一个 System.Object,第二个参数是 System.EventArgs的子类型
             * System.Object:表示一个对发送事件对象(例如上面的Car对象)的引用,第二个参数则表示与该事件相关的信息
             * System.EventArgs基类:表示一个不发送任何自定义信息的事件
             *
             * 对于简单的事件来说,我们可以直接传递一个EventArgs的实例,但如果需要传递自定义数据,应该构建一个派生自EventArgs的类,
             * 假定:有一个AnimalEventArgs的类,它保存一个字符串,表示要发送给接收者的信息
             */

创建一个AnimalEventArgs。派生自EventArgs

  //派生自EventArgs的类,它保存一个字符串,表示要发送给接收者的信息
    class AnimalEventArgs : EventArgs
    {
        public readonly string msg;
        public AnimalEventArgs(string message)
        {
            msg = message;
        }
    }

我们直接在上面的Animal类中修改。为了不改动之前的代码。直接在Animal类中添加相应的委托,事件和方法

 //修改委托:以符合微软推荐的事件模式
        public delegate void AnimalEngineHandler1(object sender, AnimalEventArgs e);
   //修改
        public event AnimalEngineHandler1 Call1;
 //提示方法
        public void showMsg1(string name)
        {
            if (Call1 != null)
            {
                if (name == "大熊猫") Call1(this, new AnimalEventArgs("杀了它你就是国宝"));
                else Call1(this, new AnimalEventArgs("^-^"));
            }
        }

最后编写测试代码

#region 创建自定义的事件参数
            //创建自定义的事件参数
            /*
                微软推荐的事件模式:第一个参数是一个 System.Object,第二个参数是 System.EventArgs的子类型
             * System.Object:表示一个对发送事件对象(例如上面的Car对象)的引用,第二个参数则表示与该事件相关的信息
             * System.EventArgs基类:表示一个不发送任何自定义信息的事件
             *
             * 对于简单的事件来说,我们可以直接传递一个EventArgs的实例,但如果需要传递自定义数据,应该构建一个派生自EventArgs的类,
             * 假定:有一个AnimalEventArgs的类,它保存一个字符串,表示要发送给接收者的信息
             */
            //Animal a = new Animal();
            //a.Call1 += AnimalMsg1;
            //a.showMsg1("大熊猫");
            #endregion

泛型EventHandler<T>委托

 由于很多自定义委托接受object作为第一个参数,EventArgs派生类型作为第二个参数,我们可以通过使用泛型EventHandler<T>类型来进一步简化之前的示列
   其中T就是自定义EventArgs类型,

同样,在Animal中新增下面测试代码

//使用泛型EventHandler<T>委托,就不在需要定义一个自定义委托类型
        public event EventHandler<AnimalEventArgs> eventHandlerDemo;
 //提示方法
        public void showMsg3(string name)
        {
            if (eventHandlerDemo != null)
            {
                if (name == "大熊猫") eventHandlerDemo(this, new AnimalEventArgs("杀了它你就是国宝"));
                else eventHandlerDemo(this, new AnimalEventArgs("^-^"));
            }
        }

 编写测试代码

 

  #region 泛型EventHandler<T>委托
            //泛型EventHandler<T>委托
            /*
                由于很多自定义委托接受object作为第一个参数,EventArgs派生类型作为第二个参数,我们可以通过使用泛型EventHandler<T>类型来进一步简化之前的示列
             * 其中T就是自定义EventArgs类型,
             */
            Animal a3 = new Animal();
            //EventHandler<AnimalEventArgs> a5 = new EventHandler<AnimalEventArgs>(AnimalMsg1);
            //a3.eventHandlerDemo += a5;

            //简写
            //a3.eventHandlerDemo += AnimalMsg1;

            //a3.showMsg3("大熊猫");
            #endregion

C# 匿名方法

 #region C# 匿名方法
            //C# 匿名方法
            /*
             * 可以看到,当一个调用者想监听传进来的事件时,它必须定义一个唯一的与相关联委托签名匹配的方法
             * 这样的方法,很少会被调用委托之外的任何程序所调用,从生产效的角度来说,手工定义一个由委托对象调用的方法显得有点繁琐,不会很受欢迎。
             * 那么。可以在事件注册时,直接将一个委托与一段代码相关联,这种代码的正式名称为匿名方法(即没有方法名称)
             */
            //匿名方法中最后一个大括号必须以分号结束,否则,将产生一个编译错误
            //a3.eventHandlerDemo += delegate(object sender, AnimalEventArgs e)
            //{
            //    Console.WriteLine(e.msg);
            //};

            //int c = 90; //匿名方法的外部变量==》定义匿名方法的本地变量,称为匿名方法的外部变量

            //如果不需要接收由事件发送的传入参数,则
            //a3.eventHandlerDemo += delegate
            //{
            //    Console.WriteLine("不需要接收由事件发送的传入参数");

            //    /*
            //        匿名方法注意事项
            //     * 1:匿名方法不能访问定义方法中的ref或out参数
            //     * 2:匿名方法中的本地变量不能与外部方法中的本地变量重名
            //     * 3:匿名方法可以访问外部类作用域中的实例变量或静态变量
            //     * 4:匿名方法内的本地变量可以与外部类的成员变量同名,因为本地变量的作用域不同,可以隐藏外部类的成员变量
            //     */
            //    //int c = 8;//匿名方法中的本地变量不能与外部方法中的本地变量重名
            //    Console.WriteLine(c); //访问匿名方法的外部变量
            //};
            //a3.showMsg3("大熊猫");
            #endregion

Lambda 表达式

 //Lambda 表达式
            /*
             * 我们知道C#支持内联处理方法,通过直接把一段代码语句赋值给事件,即:匿名方法,Lambda表达式可以用更简单的方式来写匿名方法,彻底简化了对.net委托类型的使用
             */
            //如果没有参数的委托交互,可以使用空括号表示表达式的参数列表
            a3.eventHandlerDemo += (sender, e) => {
                Console.WriteLine(e.msg);
            };
            a3.showMsg3("大熊猫"); 

我是在vs中边测试边记录的。每个模块都用一个 region 区域别包含了。你可以把所有region都注释。然后从上面取消一个一个的 region 里面的代码来注释,看效果

下面是全部源码,你可以复制到自己的vs中

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Linq;
  4 using System.Text;
  5
  6 namespace DeleagateDemo
  7 {
  8     /*委托是一个类型安全的对象
  9         * 它指向程序中另一个以后被调用的方法或多个方法。委托类型包含3个重要的信息:
 10         * 1:它所调用的方法的名称
 11         * 2:该方法的参数(可选)
 12         * 3:该方法的返回值(可选)
 13         * 注:.net委托既可以指向静态方法,也可以指向实例方法
 14         *
 15         * 当一个委托对象被创建并提供了上述信息后,它可以在运行时动态调用其他指向的方法,可以看到 .Net Framework 中每个委托(包含自定义委托)
 16         * 都被自动赋予同步或异步访问方法的能力,可以不用手工创建与管理一个Thread对象而直接调用另一个辅助线程上的方法。大大简化了编程工作
 17      */
 18     class Program
 19     {
 20         //这个泛型委托可以调用任何返回void并接受单个参数的方法
 21         public delegate void myGenericDelegate<T>(T tag);
 22
 23
 24         //这个委托可以指向任何传入两个整数返回一个整数的方法
 25         public delegate int BinaryOp(int x, int y);
 26
 27         static void Main(string[] args)
 28         {
 29
 30             /*当C#编译处理委托类型时,它先自动生成一个派生自 System.MulticastDelegate的密封类(BinaryOp)如图
 31              * 这个类与它的基类System.Delegate一起为委托提供必要的基础设施,以维护(委托指向的方法)
 32              * 以后将要调用方法的列表。
 33              * 我们生成项目后。把.exe文件拖到Reflector中
 34              *
 35              *
 36              * 可以看到生成后,Program类定义了3个公共方法
 37              * 1:Invoke()是核心方法,因为他被用来以同步方式调用委托对象维护(委托指向的方法)的每个方法
 38              * 这里的同步就是指被调用者必须等待调用完成才 能继续执行
 39              * 2:BeginInvoke()和EndInvoke()方法能在第二个执行线程上异步调用当前方法
 40              *
 41              * 那么编译器又是如何确切知道怎样定义Invoke()、BeginInvoke()和EndInvoke()方法的呢?
 42              * 把图中的BinaryOp分解如下:sealed代表密封
 43              * sealed class BinaryOp:System.MulticastDelegate
 44              * {
 45              *      public int Invoke(int x,int y);
 46              *      public IAsyncResult BeginInvoke(int x,int y,AsyncCallback callback,object state);
 47              *      public int EndInvoke(IAsyncResult result);
 48              * }
 49              * 解:
 50              * Invoke()方法定义的参数和返回值完全匹配 BinaryOp 委托的定义。
 51              * BeginInvoke()方法的参数(这里是两个整数)也基于 BinaryOp 委托,但BeginInvoke总是提供最后两个参数(AsyncCallback类型与object类型)用于
 52              * 异步方法调用。
 53              * 最后EndInvoke()方法的返回值与初始化委托 BinaryOp 声明相同,总是以一个实现了IAsyncResult接口的对象作为唯一的参数。
 54              *
 55              *
 56              * 同理:public delegate string MyDelegate(bool a,bool b,bool c)
 57              * 那么:
 58              * sealed class MyDelegate:System.MulticastDelegate
 59              * {
 60              *      public string Invoke(bool a,bool b,bool c);
 61              *      public IAsyncResult BeginInvoke(bool a,bool b,bool c,AsyncCallback callback,object state);
 62              *      public string EndInvoke(IAsyncResult result);
 63              * }
 64              *
 65              * 委托还可以指向包含任意数量out 或 ref参数(已经params关键字标记的数组参数)的方法
 66              *
 67              * 同理:public delegate string MyOtherDelegate(out bool a,ref bool b,int c)
 68              * 那么:Invoke()和BeginInvoke()方法都没变。但EndInvoke()方法有所变化,其中包括委托类型定义的所有out/ref参数
 69              * sealed class MyOtherDelegate:System.MulticastDelegate
 70              * {
 71              *      public string Invoke(out bool a,ref bool b,int c);
 72              *      public IAsyncResult BeginInvoke(out bool a,ref bool b,int c,AsyncCallback callback,object state);
 73              *      public string EndInvoke(out bool a,ref bool b,IAsyncResult result);
 74              * }
 75              *
 76              * 故:C#委托类型的定义会生成一个密封类,它包含3个编译器生成的方法,这3个方法的参数与返回值基于委托的声明。
 77              *
 78              * System.MulticastDelegate和System.Delegate基类
 79              *
 80              * 使用C#中delegate关键字创建委托的时候,也间接的声明了一个派生自System.MulticastDelegate的类,这个类使其继承类可以访问包含由委托对象
 81              * 维护的方法地址的列表已经一些处理调用列表的附加方法(与少数重载的操作符)
 82              *
 83              *
 84              *
 85             */
 86
 87             #region 一个简单的委托事例
 88             /*一个简单的委托事例*/
 89
 90             //创建一个指向SimpleMath.Add()方法的BinaryOp对象
 91             //BinaryOp b = new BinaryOp(SimpleMath.Add);
 92
 93             /*使用委托对象间接调用Add()方法  底层:Invoke()在这里被调用了,可以:b.Invoke(10,10),不过我们不需要显示的调用Invoke();
 94              * 在底层,运行库实际上在MulticastDelegate派生类上调用了编译器生成的Invoke()方法
 95              */
 96             //Console.WriteLine("10+10 is {0}", b(10, 10));  //20
 97             #endregion
 98
 99
100             //使用委托发送对象状态通知
101             #region 使用委托发送对象状态通知
102             /*
103             Console.WriteLine("小汽车上路。。。。\n");
104
105             //首先:创建一个Car对象
106             Car c1 = new Car("BMW", 100, 10);
107
108             //现在:告诉汽车,它想要想我们发送信息时调用哪个方法
109             c1.RegisterWithCarEngine(new Car.CarEngineHandler(OnCarEngineEvent));
110
111             //更简单的写法(方法组转换语法),即 在调用以委托作为参数的方法时,直接提供方法的名称,而不用创建委托对象
112             c1.RegisterWithCarEngine(OnCarEngineEvent);
113
114             //然后:加速,将会触发事件
115             Console.WriteLine("**加速***");
116             for (int i = 0; i < 6; i++)
117             {
118                 c1.Accelerate(20);
119             }
120             */
121             #endregion
122
123             #region 委托协变
124             //委托协变,你有没有想过,如果委托类型指向返回自定义类类型的方法?
125             /*
126              * 说白了协变和逆变也就是,创建一个委托,指向多个方法,方法的参数是存在继承关系的对象,之间的一个转换
127
128              */
129             /*Person tag = new Person();
130             tag.Register(GetPer);
131
132             Person p = tag;
133             tag.show();
134
135             Person ct = new China();
136             //协变允许这种目标对象赋值,
137             ct.Register(GetChi);
138             //委托本来是返回Person类型的。但因为协变,做了一个显示强制类型转换,这里得到派生类 类型的方法,
139             China c = (China)ct;
140             c.show();
141              */
142
143             #endregion
144
145
146
147             #region 泛型委托
148             //泛型委托
149             /*
150                 疑问:如果你希望定义一个委托类型来调用任何返回void并且接受单个参数的方法,但这个参数类型可能会不同,那么,就可以通过类型参数来构建 泛型委托
151              *
152              * myGenericDelegate<T>定义了一个类型参数表示要传入委托目标的实参,在创建类型实例时,需要指定类型参数的值以及委托调用的方法的名称。如:
153              */
154             //如果使用字符串作为类型参数
155             //myGenericDelegate<string> strTarget = new myGenericDelegate<string>(StringTarget);
156             //strTarget("字符串类型参数");
157             #endregion
158
159             #region C# 事件
160             //C# 事件
161             /*
162                 从上面使用的委托来看,使用委托会有一些重复的代码,当定义委托,需要声明必要的成员变量已经创建自定义的注册/注销方法来保护封装。
163              * 如果没有把委托定义为私有的,那么调用者就可以直接访问委托对象,这样调用者就可以把变量重新赋值为新的委托对象,实际上也就删除了当前要调用的方法列表。
164              * 也就是说,调用者可以直接调用委托列表
165              */
166             //传入监听的事件,调用者只需要使用+=和-=操作符即可
167             //Animal.AnimalEngineHandler c = new Animal.AnimalEngineHandler(AnimalMsg);
168             //Animal a = new Animal();
169             //a.Call += c;
170             //a.showMsg("dd");
171
172             Animal an = new Animal();
173             //an.Call += new Animal.AnimalEngineHandler(AnimalMsg);
174             //更简单的方法,利用方法组转换
175             //an.Call += AnimalMsg;
176             //an.showMsg("熊猫");
177             #endregion
178
179             #region 创建自定义的事件参数
180             //创建自定义的事件参数
181             /*
182                 微软推荐的事件模式:第一个参数是一个 System.Object,第二个参数是 System.EventArgs的子类型
183              * System.Object:表示一个对发送事件对象(例如上面的Car对象)的引用,第二个参数则表示与该事件相关的信息
184              * System.EventArgs基类:表示一个不发送任何自定义信息的事件
185              *
186              * 对于简单的事件来说,我们可以直接传递一个EventArgs的实例,但如果需要传递自定义数据,应该构建一个派生自EventArgs的类,
187              * 假定:有一个AnimalEventArgs的类,它保存一个字符串,表示要发送给接收者的信息
188              */
189             //Animal a = new Animal();
190             //a.Call1 += AnimalMsg1;
191             //a.showMsg1("大熊猫");
192             #endregion
193
194             #region 泛型EventHandler<T>委托
195             //泛型EventHandler<T>委托
196             /*
197                 由于很多自定义委托接受object作为第一个参数,EventArgs派生类型作为第二个参数,我们可以通过使用泛型EventHandler<T>类型来进一步简化之前的示列
198              * 其中T就是自定义EventArgs类型,
199              */
200             Animal a3 = new Animal();
201             //EventHandler<AnimalEventArgs> a5 = new EventHandler<AnimalEventArgs>(AnimalMsg1);
202             //a3.eventHandlerDemo += a5;
203
204             //简写
205             //a3.eventHandlerDemo += AnimalMsg1;
206
207             //a3.showMsg3("大熊猫");
208             #endregion
209
210             #region C# 匿名方法
211             //C# 匿名方法
212             /*
213              * 可以看到,当一个调用者想监听传进来的事件时,它必须定义一个唯一的与相关联委托签名匹配的方法
214              * 这样的方法,很少会被调用委托之外的任何程序所调用,从生产效的角度来说,手工定义一个由委托对象调用的方法显得有点繁琐,不会很受欢迎。
215              * 那么。可以在事件注册时,直接将一个委托与一段代码相关联,这种代码的正式名称为匿名方法(即没有方法名称)
216              */
217             //匿名方法中最后一个大括号必须以分号结束,否则,将产生一个编译错误
218             //a3.eventHandlerDemo += delegate(object sender, AnimalEventArgs e)
219             //{
220             //    Console.WriteLine(e.msg);
221             //};
222
223             //int c = 90; //匿名方法的外部变量==》定义匿名方法的本地变量,称为匿名方法的外部变量
224
225             //如果不需要接收由事件发送的传入参数,则
226             //a3.eventHandlerDemo += delegate
227             //{
228             //    Console.WriteLine("不需要接收由事件发送的传入参数");
229
230             //    /*
231             //        匿名方法注意事项
232             //     * 1:匿名方法不能访问定义方法中的ref或out参数
233             //     * 2:匿名方法中的本地变量不能与外部方法中的本地变量重名
234             //     * 3:匿名方法可以访问外部类作用域中的实例变量或静态变量
235             //     * 4:匿名方法内的本地变量可以与外部类的成员变量同名,因为本地变量的作用域不同,可以隐藏外部类的成员变量
236             //     */
237             //    //int c = 8;//匿名方法中的本地变量不能与外部方法中的本地变量重名
238             //    Console.WriteLine(c); //访问匿名方法的外部变量
239             //};
240             //a3.showMsg3("大熊猫");
241             #endregion
242
243
244             //Lambda 表达式
245             /*
246              * 我们知道C#支持内联处理方法,通过直接把一段代码语句赋值给事件,即:匿名方法,Lambda表达式可以用更简单的方式来写匿名方法,彻底简化了对.net委托类型的使用
247              */
248             //如果没有参数的委托交互,可以使用空括号表示表达式的参数列表
249             a3.eventHandlerDemo += (sender, e) => {
250                 Console.WriteLine(e.msg);
251             };
252             a3.showMsg3("大熊猫");
253             Console.ReadLine();
254
255
256
257         }
258
259         public static Person GetPer()
260         {
261             return new Person();
262         }
263         public static China GetChi()
264         {
265             return new China();
266         }
267         //要传入的事件方法
268         public static void OnCarEngineEvent(string msg)
269         {
270             Console.WriteLine("\n***报警信息*****");
271             Console.WriteLine("信息提示 => {0}", msg);
272             Console.WriteLine("\n********");
273         }
274
275         static void StringTarget(string tag)
276         {
277             Console.WriteLine("传来的字符串是 {0}", tag);
278         }
279         static void IntTarget(int tag)
280         {
281             Console.WriteLine("传来的整数是 {0}", tag);
282         }
283         static void AnimalMsg(string msg)
284         {
285             Console.WriteLine(msg);
286         }
287         /// <summary>
288         ///
289         /// </summary>
290         /// <param name="sender">发送事件对象的引用</param>
291         /// <param name="e">表示与该事件相关的信息</param>
292         static void AnimalMsg1(object sender, AnimalEventArgs e)
293         {
294             /*如果接收者想与发送事件的对象交互,我们可以显示强制类型转换System.Object,
295              * 这样就可以使用传递给事件通知对象中的任何公共成员
296             */
297             //为了安全起见,这里可以强制转换前做一次运行时检查
298             if (sender is Animal)
299             {
300                 Animal a = sender as Animal;
301                 Console.WriteLine("name is {0},message is {1}", a.n, e.msg);
302             }
303         }
304     }
305     public class SimpleMath
306     {
307         //这个类包含了BinaryOp将指向的方法
308         public static int Add(int x, int y)
309         {
310             return x + y;
311         }
312     }
313
314     /*
315         *1:定义将通知发送给调用者的委托类型
316         * 2:声明Car类中每个委托类型的成员变量
317         * 3:在Car上创建辅助函数使调用者能指定由委托成员变量保存的方法
318         * 4:修改Accelerate()方法在适当的情形下调用委托的调用列表
319     */
320     class Car
321     {
322
323         //1:定义委托类型
324         public delegate void CarEngineHandler(string msgForCaller);
325
326         //2:定义每个委托类型的成员变量
327         private CarEngineHandler listOfHandlers;
328
329         //3:向调用者添加注册函数,自定义的注册方法封装的私有委托成员变量
330         public void RegisterWithCarEngine(CarEngineHandler methodToCall)
331         {
332             listOfHandlers = methodToCall;
333
334             #region 多路广播
335             //委托支持多路广播,即一个委托对象可以维护一个可调用方法的列表而不只是单独一个方法,给一个委托对象添加多个方法时,不用直接分配(赋值操作符 = ),重载+=操作符即可
336             //listOfHandlers += methodToCall; (底层是使用的Delegate.Combine()方法)
337             //注:既然+=操作符 是添加方法 那么-=操作符则是从委托调用列表中移除成员 (底层是使用的Delegate.Remove()方法)
338             #endregion
339         }
340
341         /*
342            注:将委托定义在使用它的类型作用域里是很普遍的
343         * 严格来说,可以将委托成员变量定义为公共的,这样就不需要创建额外的注册方法,然而,如果将委托成员定义为私有的。我们就强制了封装服务并提供了更加类型安全的解决方案。
344          * 比如:C# event关键字,声明为事件
345         */
346
347
348
349         //内部状态数据
350         public int CurrentSpeed { get; set; } //当前速度
351         public int MaxSpeed { get; set; } //最大速度
352         public string PetName { get; set; } //小汽车名字
353
354         //汽车能用还是不能用
355         private bool carIsDead;
356
357         //类的构造函数
358         public Car() { MaxSpeed = 100; }
359         public Car(string name, int maxSp, int currSp)
360         {
361             CurrentSpeed = currSp;
362             MaxSpeed = maxSp;
363             PetName = name;
364         }
365
366         //此方法:使Car对象向订阅者发送引擎相关的信息。
367         public void Accelerate(int delta)
368         {
369             //当前汽车是否不能用
370             if (carIsDead)
371             {
372                 //判断当前是否有订阅者
373                 if (listOfHandlers != null)
374                 {
375                     listOfHandlers("您的汽车已经报废");
376                 }
377             }
378             else
379             {
380                 CurrentSpeed += delta;
381
382                 //假设。距离最大速度剩下10 则发送警报信息
383                 if (10 == (MaxSpeed - CurrentSpeed) && listOfHandlers != null)
384                 {
385                     listOfHandlers("不能在加速了,滴滴哒哒");
386                 }
387                 if (CurrentSpeed >= MaxSpeed) carIsDead = true;
388                 else Console.WriteLine("当前速度 = {0}", CurrentSpeed);
389             }
390         }
391     }
392
393     class China : Person
394     {
395         public void show()
396         {
397             if (tag == null) Console.WriteLine("派生类没有注册");
398             else Console.WriteLine("派生类已经注册");
399         }
400     }
401
402     class Person
403     {
404         public delegate Person DelegateHander();
405         protected DelegateHander tag;
406
407         public void Register(DelegateHander meth)
408         {
409             tag = meth;
410         }
411         public void show()
412         {
413             if (tag == null) Console.WriteLine("父类没有注册");
414             else Console.WriteLine("父类已经注册");
415         }
416     }
417
418     //派生自EventArgs的类,它保存一个字符串,表示要发送给接收者的信息
419     class AnimalEventArgs : EventArgs
420     {
421         public readonly string msg;
422         public AnimalEventArgs(string message)
423         {
424             msg = message;
425         }
426     }
427
428     class Animal
429     {
430         /*
431             定义一个事件分为两个步骤:
432          * 1:需要定义一个委托类型,它包含在事件触发时将要调用的方法
433          * 2:通过Event关键字用相关委托声明这个事件
434          */
435
436         //这个委托用来与Animal的事件协作
437         public delegate void AnimalEngineHandler(string name);
438
439         //修改委托:以符合微软推荐的事件模式
440         public delegate void AnimalEngineHandler1(object sender, AnimalEventArgs e);
441
442         //使用泛型EventHandler<T>委托,就不在需要定义一个自定义委托类型
443         public event EventHandler<AnimalEventArgs> eventHandlerDemo;
444
445
446         //声明事件
447         public event AnimalEngineHandler Call;
448
449         //修改
450         public event AnimalEngineHandler1 Call1;
451
452         //提示方法
453         public void showMsg(string name)
454         {
455             if (Call != null)
456             {
457                 if (name == "熊猫") Call("杀了它你就是国宝!");
458                 else Call("^-^");
459             }
460         }
461         //提示方法
462         public void showMsg1(string name)
463         {
464             if (Call1 != null)
465             {
466                 if (name == "大熊猫") Call1(this, new AnimalEventArgs("杀了它你就是国宝"));
467                 else Call1(this, new AnimalEventArgs("^-^"));
468             }
469         }
470         //提示方法
471         public void showMsg3(string name)
472         {
473             if (eventHandlerDemo != null)
474             {
475                 if (name == "大熊猫") eventHandlerDemo(this, new AnimalEventArgs("杀了它你就是国宝"));
476                 else eventHandlerDemo(this, new AnimalEventArgs("^-^"));
477             }
478         }
479         public string n = "测试";
480     }
481 }
时间: 2024-10-04 03:59:25

委托基础详解的相关文章

Swift学习——Swift基础详解(四)

A:小儿编程很不好! B:多半是不爱学,从看英文版开始,让你爱上编程! Type Aliases    类型重定义(typedef) Swift中重定义类型的关键字是typealias,至于怎么用,应该不必多说了,看例子: typealias AudioSample = UInt16 //定义了一个类型名称AudioSample,代表UInt16类型 var maxAmplitudeFound = AudioSample.min // maxAmplitudeFound is now 0 Boo

Swift学习——Swift基础详解(一)

注:由于基础部分在Swift Tour 中已经大体的说明了,所以在详解中不会达到100%的原文释义 Constants and Variables  常量和变量 常量和变量都需要声明名称和类型(作为程序员,这些基础也就不说了),常量一次赋值不能改变,变量的值可以改变 Declaring Constants and Variables   声明常量和变量 常量和变量在使用之前必须要声明,使用let关键字定义常量,var关键字定义变量 下面的例子可以用来定义用户登录的时候最大的尝试次数: let m

Swift学习——Swift基础详解(二)

上节说了没有营养的变量和常量,这玩意,都差不多,自己稍微看下就好了 Integers    整型 整数就是整数了,没有小数,整数有符号(+,-,0)或者无符号(0,+) Swift提供了8,16,32,64位的有符号和无符号的整数,命名使用C的方式,比如,8位无符号的整型UInt8,32位有符号的整型就是Int32 Integer Bounds    整型范围 可以使用min 和 max获取整数类型的最大值和最小值 let minValue = UInt8.min // minValue is

HAProxy:基础详解

一.简介 HAProxy提供高可用性.负载均衡以及基于TCP和HTTP应用的代理,支持虚拟主机,它是免费.快速并且可靠的一种解决方案.HAProxy特别适用于那些负载特大的web站点,这些站点通常又需要会话保持或七层处理.HAProxy运行在时下的硬件上,完全可以支持数以万计的并发连接.并且它的运行模式使得它可以很简单安全的整合进您当前的架构中, 同时可以保护你的web服务器不被暴露到网络上. HAProxy实现了一种事件驱动.单一进程模型,此模型支持非常大的并发连接数.多进程或多线程模型受内存

Swift学习——Swift基础详解(三)

小葵花课堂继续开讲 Numeric Literals    数字文本 数字文本有以下几种写法: A decimal number, with no prefix A binary number, with a 0b prefix An octal number, with a 0o prefix A hexadecimal number, with a 0x prefix 十进制数,无前缀:二进制数,0b前缀:八进制数,0o前缀:十六进制数,0x前缀 论数字17的N种表现形式: let deci

Swift学习——Swift基础详解(八)

Assertions    断言 可选可以让你判断值是否存在,你可以在代码中优雅地处理值缺失的情况.然而,在某些情况下,如果值缺失或者值并不满足特定的条件,你的代码可能并不需要继续执行.这时,你可以在你的代码中触发一个断言(assertion)来结束代码运行并通过调试来找到值缺失的原因. Debugging with Assertions    使用断言进行调试 断言会在运行时判断一个逻辑条件是否为true.从字面意思来说,断言"断言"一个条件是否为真.你可以使用断言来保证在运行其他代

Swift学习——Swift基础详解(六)

Optionals    选配 选配(好像不是这么翻译的)适用于那些值可能为空的情况,一个选配有两种情况:存在值并且等于x,或者值不存在. 选配的概念在OC和C里面并没有,在OC中最接近的概念就是:OC中的返回值为对象的函数,当对象不存在的时候,可以返回nil,但是nil只能代表对象,不可以代表基础类型或者结构体和枚举类型.OC中使用NSNotFound表示值不存在.在Swift中,不需要使用其他的类型表示不存在的值. 举个例子: 在Swift中,String类型有一个方法叫做toInt,但是并

Android BroadcastReceiver基础详解一

-.BroadcastReceivcer概述 1.什么是广播 BroadcastReceiver是Android四大组件之一,本质是一种全局的监听器,用于监听系统全局的广播消息.因此它可以非常方便的实现不同组件之间的通信. 2.BroadcastReceiver的创建启动 BroadcastReceiver是用用于接受程序所放出的Broadcast Intent,与应用程序启动的Activity.Service相同.也只需要两步: ①.创建需要启动的Broadcast的Intent ②.创建一个

haproxy 基础详解 及 动静分离的实现

haproxy 介绍 1 工作在ISO 七层 根据http协议(或者工作在ISO四层 根据tcp协议) 提供web服务的负载均衡调度器 负载均衡调度器分类 工作在四层: # lvs 工作在七层: # nginx (web,http reverse proxy,cache) # haproxy (http reverse proxy,tcp proxy) # tcp: 实现MySQL的读写中读的负载均衡 # ats (apache traffic server) # perlbal # pound