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;}
- 1楼学文练武
- quot; 当要把方法传递给其他方法时,需要使用委托quot;对这句话没太理解,如果一个方法A要调用另一个方法B,在方法A中调用方法B就可以了,何必用委托,能不能举一个例子详细说明,只有在运行时才知道方法B,谢谢
- Re: C#ai鸟
- @学文练武,比如线程调用一个方法,设一个方法为Method函数,我们调用时就需要Thread th =new Thread(Method)来调用,这就是一种委托,不过委托的声明早就被微软封装到Thread类中,th.Start()来触发执行Method方法。