委托

网上有很多关于委托的定义,MSDN官方给的定义是

    • 委托类似于 C++ 函数指针,但它们是类型安全的。

    • 委托允许将方法作为参数进行传递。

    • 委托可用于定义回调方法。

    • 委托可以链接在一起;例如,可以对一个事件调用多个方法。

    • 什么是C++函数指针?个人对C++不熟悉,所以理解可能有误,指向一个方法。其实我个人的理解是

    • 委托实际上是定义了一个方法规范,类似具有某个规范的方法接口。任何具有此规范的方法都可以用来实例化这个定义规范的委托,实际上我们想执行的还是方法,而委托只是一个中间人,一个规范定义人,那么为什么我们不直接使用方法呢?举两个例子,一个是现实生活中的,一个程序中的

    • 1,我们去电影院看电影,买一张票只能看某个固定时间,固定场次的电影,一旦过了电影时间可能就看不了,在这里我们可以看成

    • bool 我们.看电影(参数 电影票,参数 电影名称,参数时间,参数 人数)这是一个固定方法

    • 我们给方法传入参数,就是我们去买票得到的一系列信息。电影是一直会放的,所以就会工作人员来调用我们的这个方法,看看我们是否可以通过进去观看电影,如果true就可以进去,如果是false就不能进去。这就是硬编码。但是如果我们不想在固定的时间观看固定的电影呢,对,我们就可以用委托,我们只需要预留这样的一个规范,只要符合上述的方法规范,只要结果返回true,我们就可以在任何时间,观看任何电影。这就大大提高了灵活性。我们可以通过很多途径来满足这个规范,比如买电影兑换券。

    • 在程序中最典型的委托可能就是事件了,因为每个触发事件所需要的功能是不一样的,如果我触发一个Click事件我想让我们变成李嘉诚,当然是在程序里,另外一个人想变成蜘蛛侠,这在程序里都可以实现。所以这个地方为了满足我们所有人变身的想法我们就必须预留一个规范,在这里就是事件的规范,至于实现这个规范的代码想变成什么完全是由我们自己来定义。这就是行为规范,只要实现了我的规范,至于你想怎么实现,实现什么都是由自己决定。所以在程序中为什么要使用委托,我的理解是,在程序的某个地方,我们需要的只是一个方法规范,为什么是方法规范,因为这个地方可能有太多的不确定因素,或者认为的想造成不确定因素,以至于我们想再任何地方,任何时间来实现这个不确定因素达到确定

    • 委托实际上是定义了一个方法规范,类似具有某个规范的方法接口。任何具有此规范的方法都可以用来实例化这个定义规范的委托,实际上我们想执行的还是我们符合这个规范的工功能,而委托只是一个中间人,一个规范定义人,只要符合这个规范都可以实现功能,最重要的一点是,委托是类型安全的。因为它不需要我们去物理寻址。

    • 概念谈完了,我们现在来看看在代码级别怎么实现,要确定的是,委托也是一个类型

    • 1,我们要定义个委托

      • public delegate int OperNumber(int firstNumber,int lastNumber);

      刚刚谈到,委托实际上也是一个类型,跟我们见过的String,Array一样都是 类型,所以它也有父类,它的父类是System.MulticastDelegate,而System.MulticastDelegate又继承自System.Delegate.这里我们就定义了一个委托,我更习惯说是定义了一个规范,这个规范是

      1返回值int

      2,参数1,int。参数2,int

      3,两个参数。

      这里就定义了这样一个规范,我在这里不想实现某个特定的功能,我只想留一个“接口”,至于你想怎么操作这两个数完全由你决定,只要你符合我的规范就行.

      下面我们就需要有一个实现这个规范的方法来把自身交给个规范,就是实例化一个委托。

      新建类OperationNumber

         public class OperationNumber
          {
             public int AddTwoNumber(int firstNumber,int lastNumber)
             {
                 return firstNumber + lastNumber;
             }
          }
      

      类里面有一个方法,这个方法实现了我前面定义的规范,返回值,参数,参数个数。这里实现的是相加操作。满足了这个规范,我们就可以把规范交给规范定义者(实例化委托)。

          class Program
          {
              static void Main(string[] args)
              {
                  OperationNumber number = new OperationNumber();
                  OperNumber operNumber = new OperNumber(number.AddTwoNumber);
              }
          }
      

      剩下的就是调用委托了

          class Program
          {
              static void Main(string[] args)
              {
                  OperationNumber number = new OperationNumber();
                  OperNumber operNumber = new OperNumber(number.AddTwoNumber);
                  Console.WriteLine(operNumber(3,5));
                  Console.Read();
              }
          }
      

      传入参数,输出8.实际上也是在调用方法,只是给了方法很大的灵活性。

      这里几 个问题

      1,委托是支持异步模式的,以后再别的文章中,我会写到,这里也可以operNumber.Invoke(3,5)这样调用委托,Invoke方法有 BeginInvoke和EndInvoke模式。

      2,在实例化委托的时候我们可以直接传送方法名称OperNumber operNumber = number.AddTwoNumber;//在.net 2.0中加入。直接用方法名实例化委托

      3,委托需要传递的方法名称,而不是调用一个方法,如果这里写成OperNumber operNumber = new OperNumber(number.AddTwoNumber(3,8));编译就会不通过

      多播委托

多播委托就是委托可以调用多个方法,多播委托里的方法返回值应该为Void,如果不是Void,就只能得到最后一个方法的结果,下面我们改动之前的代码

   public class OperationNumber
    {
       public void AddTwoNumber(int firstNumber,int lastNumber)
       {
           int result =firstNumber + lastNumber;
           Console.WriteLine(result);
       }
       public void MultiplyNumber(int firstNumber, int lastNumber)
       {
           int result = firstNumber * lastNumber;
           Console.WriteLine(result);
       }
       public int DivideNumber(int firstNumber, int lastNumebr)
       {
           int result = firstNumber / lastNumebr;
           return result;
       }
       public int Minus(int firstNumber, int lastNumber)
       {
           int result = firstNumber - lastNumber;
           return result;
       }
    }

定义了4个方法,前两个返回Void,后两个返回int

我们使用+=把一个方法添加进委托里

    class Program
    {
        static void Main(string[] args)
        {
            OperationNumber number = new OperationNumber();
            OperNumber operNumber = new OperNumber(number.AddTwoNumber);
            operNumber += number.MultiplyNumber;
            operNumber(3, 8);
            Console.Read();
        }
    }

输出结果为11,24,委托里面的方法依次执行,下面我们定义另外一个委托 public delegate int OperNumber(int firstNumber,int lastNumber);
调用代码改为

 class Program
    {
        static void Main(string[] args)
        {
            OperationNumber number = new OperationNumber();
            OperNumber operNumber = new OperNumber(number.Minus);
            operNumber += number.DivideNumber;
            Console.WriteLine(operNumber(4, 2));
            Console.Read();
        }
    }

输出2,当方法的返回值不为Void时,我们调用多播委托只能得到一个结果,就是最后一个方法的结果。

多播委托里方法的执行是依次的,就是说从第一个到最后一个,如果某个方法出现了异常,那后面的方法还会执行吗?我们修改代码,在第一个方法里抛出一个异常

       public void AddTwoNumber(int firstNumber,int lastNumber)
       {
           
              int result = firstNumber + lastNumber;
              Console.WriteLine(result);
             throw new Exception("Edrick's Exception");
       }

调用代码

static void Main(string[] args)
        {
            OperationNumber number = new OperationNumber();
            OperNumber operNumber = new OperNumber(number.AddTwoNumber);
            operNumber += number.MultiplyNumber;
            try
            {
                operNumber(4, 2);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
            Console.Read();
        }

输出6,Edrick's Exception

委托里的第二个方法没有执行,因为遇到异常,但是这并不是我们想要的结果,我们需要的是第一个代码抛出异常后第二个方法也能执行。所以我们这里需要手动迭代多播委托里的方法

class Program
    {
        static void Main(string[] args)
        {
            OperationNumber number = new OperationNumber();
            OperNumber operNumber = new OperNumber(number.AddTwoNumber);
            operNumber += number.MultiplyNumber;

            Delegate[] delegates = operNumber.GetInvocationList();

            foreach (OperNumber d in delegates)
            {
                try
                {
                    d(4, 2);
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message);
                }
            }

            Console.Read();
        }

这样,代码就能按照我们预计的方式运行。

匿名方法

我们在实例化委托的时候,需要传入方法的名称,而且方法必须实际存在,这里,如果我们不想传入一个实际存在的一个方法,只想传入一段实现代码。匿名方法其实就是一段实现代码,没有方法签名。

    class Program
    {
        static void Main(string[] args)
        {
            OperationNumber number = new OperationNumber();
            OperNumber operNumber = delegate(int firstNumber,int lastNumber) 
            { 
                Console.WriteLine(firstNumber * lastNumber); 
            };
            operNumber(3,3);
            Console.Read();
        }
    }

lamda表达式

我刚开始接触lamda的时候完全搞不清楚lamda是什么东西,为什么要这样实现,在了解到了匿名方法之后,才觉得真是一个突破性的突破。lamda就是匿名方法的变体

=>符号读作gosto把一个匿名方法分为两部分,左边为参数列表,右边的为实现。代码如下:

    class Program
    {
        static void Main(string[] args)
        {
            OperationNumber number = new OperationNumber();
            OperNumber operNumber = (int firstNumber, int lastNumber) =>
                {
                    Console.WriteLine(firstNumber*lastNumber);
                };
            
            operNumber(3,3);
            Console.Read();
        }
    }

实际上参数的类型可以不指定,编译器可以自动推断。如果只有一行实现代码,{}也是可以去掉的。

    class Program
    {
        static void Main(string[] args)
        {
            OperationNumber number = new OperationNumber();
            OperNumber operNumber = (firstNumber, lastNumber) => Console.WriteLine(firstNumber * lastNumber);
            operNumber(3,3);
            Console.Read();
        }
    }

在委托中,实际上还有很多,在以后的文章中会讲到泛型委托,协变和抗变。本文中的示例都不复杂,园子里又很多复杂的委托示例,想查看复杂示例的可以搜索。  

 

 

  

 

posted @ 2011-10-17 21:50  刘中栋  阅读(297)  评论(0)    收藏  举报