委托的基本理解与使用
委托本身是一种引用类型,它保存的也是托管堆中对象的引用,只不过这个引用比较特殊,他是对方法的引用。事件本身也是委托,他是委托组,C#中提供了关键字event来对事件进行特别区分的。
一、了解委托的实质:
1.委托是方法指针
2.委托是一个类,当对其进行实例化的时候,要将引用方法作为它的构造方法的参数
示例:设计一个点对点文件传输,有以下几个基本功能:
1.传输文件
2.按照百分制通知传输进度
3.传输类能同时被控制台应用程序和Winform应用程序使用
分析:由于要让通知本身能被控制台应用程序和Winform应用程序使用,因此设计的这个文件传输类在进行通知时,就不能显示地调用;
Console.WriteLine("当前进度:"+fileProgress);
理想的情况是,在需要通知的地方,全部将器置换成一个方法的指针,有调用者来决定该方法完成什么功能。这个方法指针在C#中被理解成委托。可以声明:
public delegate void FileUploadedHandler(int progress)
这个类最终形式:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class FileUploader { public delegate void FileUploadedHandler(int progress); public FileUploadedHandler FileUploaded; public void Upload() { int fileProgress = 100; while (fileProgress > 0) { //传输代码,略 fileProgress--; if (FileUploaded != null) { FileUploaded(fileProgress); } } } }
调用者在调用这个文件传输类的时候,应该同时为FileUploaded赋值。赋值过程也就是将自身所具有的的和委托声明相同的声明方法赋值给FileUploaded。这样,类型FileUploaded在执行到FileUploaded(fileProgress)语句的时候,就是在执行调用者自身的方法。
委托是一个类,可以使用ILDsam.exe 查看
在使用调用是Upload时,其实也是调用Invoke,
二、实现标准的事件模型
微软为事件模型设定的几个规范:
1.委托类型的名称以EventHandler结束
2.委托原型返回值为void
3.委托原型具有两个参数:sender表示事件触发者,e表示事件参数
4.事件参数的名称以EvenrArgs结束。
将以上程序改写为:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class FileUploadEventArgs : EventArgs { public int FileProgress { get; set; } } class FileUploader { public event EventHandler<FileUploadEventArgs> FileUploaded; public void Upload() { FileUploadEventArgs e = new FileUploadEventArgs() { FileProgress = 100 }; while (e.FileProgress > 0) { //传输代码,略 e.FileProgress--; if (FileUploaded != null) { FileUploaded(this,e); } } } }
程序调用:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
static void Main(string[] args) { FileUploader f1 = new FileUploader(); f1.FileUploaded += f1_FileUploaded; f1.Upload(); } static void f1_FileUploaded(object sender, FileUploadEventArgs e) { Console.WriteLine(e.FileProgress); } }
ILDsam查看
三、使用FCL中的委托声明
FCL中存在三类委托,分别是:Action、Func、Predicate,在泛型版本出来后,几乎能满足我们再实际编码过程中的大部分需要。
简述:
1.Action表示接受0个或多个输入参数,执行一段代码,没有任何返回值;
2.Func表示接受0个或多个输入参数,执行一段代码,带返回值;
3.Predicate表示定义一组条件并判断参数是否符合条件。
Action的重载版本有17个,分别是:
Action
Action<T>
Action<T1,T2>
……
Action<T1,T2,……T16>
Func与之类似,也有17个重载版本,只不过在泛型声明中多了一个返回值:
Func<TResult>
Func<T1,TRsult>
……
Func<T1,T2,……,T16,TResult>
示例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
static void Main(string[] args) { Func<int, int, int> add = Add; Action<string> print = Print; print(add(1, 2).ToString()); } private static void Print(string obj) { Console.WriteLine(obj); } private static int Add(int arg1, int arg2) { return arg1 + arg2; }
四、使用Lambda表达式代替方法和匿名方法
第一种刚刚使用的定义方法:
static void Main(string[] args) { Func<int, int, int> add = Add; Action<string> print = Print; print(add(1, 2).ToString()); } private static void Print(string obj) { Console.WriteLine(obj); } private static int Add(int arg1, int arg2) { return arg1 + arg2; }
第二种:最中规中矩的,最繁琐的,最基本的定义,加深对委托的认识,委托也是一种类型。
static void Main(string[] args) { Func<int, int, int> add = new Func<int, int, int>(Add); Action<string> print = new Action<string>(Print); print(add(1, 2).ToString()); } private static void Print(string obj) { Console.WriteLine(obj); } private static int Add(int arg1, int arg2) { return arg1 + arg2; }
第三种:其实针对代码量比较少的实现可以使用匿名方法
Func<int, int, int> add1 = new Func<int, int, int>(delegate(int i, int j) { return i + j; }); Action<string> print1 = new Action<string>(delegate(string msg) { Console.WriteLine(msg); });
第四种:进一步简化版本
Func<int, int, int> add1 = delegate(int i, int j) { return i + j; }; Action<string> print1 = delegate(string msg) { Console.WriteLine(msg); }; print(add(1, 2).ToString());
第五种:终极改进版,Lambda
Func<int, int, int> add1 = (i, j) => { return i + j; }; Action<string> print1 = (msg) => { Console.WriteLine(msg); }; print(add(1, 2).ToString());