你的位置:首页 > ASP.net教程

[ASP.net教程]详解C#委托

   1  委托

    当要把方法传递给其他方法时,需要使用委托。有时候某个方法执行的操作并不是针对数据进行的,而是要对另一个方法进行操作。但是编译的时候,我们不知道第二个方法是什么,这个信息只有在运行时得到,所以需要我们把第二个方法作为参数传递给第一个方法。总之,委托就是参数为方法的函数。

    事件——一般是通知代码发生了什么事件。引发了事件时,运行库需要知道应该执行哪个方法。这就要把处理事件的方法作为一个参数传递给委托。

    1.1  声明委托

    首先必须定义要使用的委托,对于委托,定义它就是告诉编译器这种类型的委托表示哪种类型的方法,然后必须创建该委托的一个或者多个实例。编译器在后台将创建表示该委托的一个类。语法如下:

    delegate void IntMethodInvoker(int x);

    在这个实例中,定义了一个委托IntMethodInvoker,并指定该委托的每个实例都可以包含一个方法的引用,该方法含有一个int参数,返回void。定义一个委托,必须给出它所表示的方法的签名和返回类型。定义委托的语法类似方法的定义,但是没有方法体,定义的前面要加上关键字delegate。可以在委托的定义上应用常用的访问修饰符:public、private、protected等。

    定义委托实际上是定义一个新类。委托实现为派生自基类System.MulticastDelegate的类,System.MulticastDelegate又派生自基类System.Delegate。C#编译器能识别这个类自动使用其委托语法,我们不需要了解这个类的具体执行情况。

    定义好委托后就可以创建它的一个实例,但创建的委托的实例仍然称为委托。

    1.2  使用委托

    下面代码说明如何使用委托,这是在int上调用ToString()方法的一种冗长方式。

  

 private delegate string GetAString();//定义委托,表示的方法不带参数,返回一个string型的值。  static void Main()  {   int x=40;      GetAString firstStringMethod=new GetAString(x.TOString);//实例化类型为GetAString的委托,并对它进行初始化,//使它引用整形变量x的ToString()方法。在C#中,委托在语法上总是接受一个参数的构造函数,这个参数就是委托引用的方法。//这个方法必须匹配最初定义委托时的签名。      Console.WriteLine("String is {0}",firstStringMethod()); //使用委托显示字符串//在任何代码中,都应该提供委托实例的名称,这里例子实例名称为firstStringMethod,后面括号中应该包含调用委托中方法时使用的任何等效参数  } 

 

    在C#编译时,编译器会用firstString.Method.Invoke()代替firstStringMethod()。(这是IDE内部实现的,不需要编程者关心,这里只是说明下它的原理。)

    为了减少代码,只需要委托实例,就可以只传送地址的名称,这称为委托推断。举例如下:

    GetAString firstStringMethod =new GetAString(x.ToString);//正常用法

    GetAString firstStringMethod=x.ToString;//委托推断的用法

    注意:千万不能输入x.ToString()。因为加()的方法是返回一个字符串对象,不加括号代表方法地址,委托调用的是方法地址。

    委托的类型是安全的,可以保证被调用的方法签名正确。

    1.3  Action<T>和Func<T>委托

    以上都是为参数和返回类型定义一个新委托。下面介绍Action<T>委托,泛型Action<T>委托表示引用一个void返回类型方法。Func<T>委托可以调用带返回类型的方法。Func<out TResult>委托类型可以调用带返回类型且无参数的方法,Func<int T,out TResult>调用一个带参数的方法。

    举例声明一个该委托类型的数组:

    Func<double ,double>[] operations=

{

   类名.方法名,类名.方法名

}

    1.4  一个委托的例子:类的冒泡法排序

   class BubbleSorter{ static public void Sort<T>(IList<T> sortArray,Func<T,T,bool>comparison)//接受类型T的泛型方法Sort<T> {   bool swapped=true;   do    {       swapped=false;       for(int i=0;i<sortArray.Count-1;i++)      {          if(comparison(sortArray[i+1],sortArray[i]))          {            T temp=sortArray[i];            sortArray[i]=sortArray[i+1];            sortArray[i+1]=temp;            swapped=true;           }              }        }while(swapped);     }}   class Employee{  public Employee(string name,decimal salary)  {     this.Name=name;this.Salary=salary;   }   public string Name{get;private set;}  public decimal Salary{get;private set;}   public static bool CompareSalary(Employee e1,Employee e2) {    return e1.Salary<e2.Salary;  }}

 

Main函数调用时:

static void Main(){   Employee[] employees={   new Employee("Buges",2000),   new Employee("Daffy",1000), new Employee("Foge",2300), new Employee("Tows",5000), new Employee("Gay",2100)};BubbleSorter.Sort(employees,Employee.ConpareSalary);//调用类的静态方法}

 

1.5多播委托

    委托可以包含多个方法,那就是多播委托。为此委托的签名必须返回void;否则就只能得到委托调用的最后一个方法的结果。多播委托可以识别运算符”+“和”+=“。

    Action<double>operation1=类名1.方法名1;

    Action<double>operation2=类名2.方法名2;

    Action<double>operationS=operation1+operation2;

    多播委托可以用”-“和”-=“以从委托中删除方法调用。

    多播委托派生自System.MulticastDelegate的类,System.MulticastDelegate又派生自基类System.Delegate。MulticastDelegate的其他成员允许把多个方法调用链接成一个列表。

    多播委托要避免依赖于特定顺序的代码。

    如果通过委托调用其中一个方法抛出异常,整个迭代就停止了。为了避免这个问题,Delegate类定义了GetInvocationList()方法,它返回一个Delegate对象数组。先中科院使用这个委托调用与委托直接相关的方法,捕获异常并继续下一次迭代。

Action d1= One;d1+=Two;Delegate[] delegates=d1.GetInvocationList();foreach (Action d in delegates){ try {  d(); } catch(Exception) {  Console.WriteLine("Exception caught"); }}

 

1.6  Lambda表达式

    只要有委托参数类型的地方,就可以使用Lambda表达式。签名使用匿名方法的例子可以改为使用Lambda表达式。Lambda运算符”=>“的左边列出了需要的参数。Lambda运算符右边定义了赋予lambda变量的方法的实现代码。

Func<string,string>oneParam=s=>String.Format("change uppercase{0}",s.ToUpper());Console.WriteLine(oneParam("test"));

 

    1.6.1  参数

Func<double,double,double>twoParamsWithTypes=(double x,double y)=>x*y;Console.WriteLine(twoParamsWithTypes(4,2));

   1.6.2 多行代码

Func<double,double>square=x=>x*x;

等价于

Func<double,double>square=x=>{  return x*x;}

添加多行语句要有括号和return语句

Func<string,string>lambda=param=>{  param+=mid;  param+=" and this was added to the string.";  return param;}