一, 委托基础
1. 什么是委托
委托(Delegate) 是存有对某个方法的引用的一种引用类型变量, 用关键字 delegate 申明, 实现相同返回值和参数的函数的动态调用, 提供了对方法的抽象.
委托 (Delegate) 特别用于实现事件和回调方法. 所有的委托 (Delegate) 都派生自 System.Delegate 类.
2. 声明委托
委托声明后决定了该委托可以引用的方法.
public delegate string MyDelegate(int i);
上面声明的委托可以用于引用任何返回值为 string 类型并且参数为一个 int 类型的任意方法名的方法, 例如下面这个:
public static string GetStr(int i){return i.ToString();}
所以委托的声明格式为: 访问修饰符 + delegate + 返回类型 + 类型名 + 参数.
3. 初始化委托变量以及赋值
声明了委托类型后, 和 string,int 等类型一样需要声明变量才能使用. 对于初始化变量的赋值有两种方式:
(1)new 实例化赋值
和一般的类实例化一样, 在构造函数中传入方法名即可.
MyDelegate mydelegate = new MyDelegate(GetStr);
(2)直接赋值
由于方法名称和相应的委托之间存在隐身转换, 所以可以等于号赋值.
MyDelegate mydelegate = GetStr;
以上例子都是直接将方法名赋值给委托变量的, 这是因为方法由 static 修饰. 对于非静态的变量还是需要实例化使用, 如下:
MyDelegate mydelegate = new People().GetStr;
由于委托是引用类型, 用 '=' 重新赋值后会改变方法的引用地址, 下述例子也继续使用静态方法.
4. 多播委托
委托对象可以通过 '+' 运算法进行组合, 一个合并委托调用所组合的委托的所有指向方法引用地址, 合并的委托必须是相同类型的. 不仅是对象可以组合, 通过 '+' 运算符可以让委托变量对方法自增.
(1)委托对象组合
- MyDelegate mydelegate1 = GetStr;
- MyDelegate mydelegate2 = GetStr;
- MyDelegate mydelegate3 = mydelegate1 + mydelegate2;
(2)自增
- MyDelegate mydelegate = GetStr;
- mydelegate += GetStr;
有增自然有减, 可以通过 '-' 运算符进行方法引用地址的剔除, 如果有多个同样的方法, 只删除其中一个.
5. 匿名方法
我们之前的方法都是事先声明好了的, 然后使用方法名. 但是有时候我们不想要声明新方法, 因为这个是一个临时的方法. 那么就可以用在 c# 2.0 版本引入的匿名方法, 或者是 3.0 以后版本的 lambda 表达式.
匿名方法使用的也是 delegate 关键字, 不需要定义返回类型, 格式为: delegate (传入参数) {执行语句}, 如下:
MyDelegate mydelegate = delegate(int i) { return i.ToString(); };
lambda 表达式简化了匿名方法的书写, 去掉了 delegate 关键字并加入 '=>' 运算符:
MyDelegate mydelegate = (int i) => { Console.WriteLine(i.ToString()); };
6. 委托调用
委托的调用和方法的调用差不多, 因为委托声明的时候参数和返回值都已经确定了, 所有加入委托调用列表的方法都是一样的, 所以当委托调用的时候所有方法会依次执行. 多播委托的情况下会返回最后一个有返回值的方法结果. 但是要注意委托变量是否为空, 空的执行会报错.
- if(mydelegate != null)
- mydelegate(1);
7. 泛型委托
泛型委托的加入使委托的使用更加方便, 简化我们的代码. 常用的泛型委托有两种: Action 和 Func, 他们的使用和一般的委托差不多, 帮我们省去了声明的哪一步.
Action 是无返回值的委托, 封装了从无参数到 16 个参数的委托.
- Action action1 = () => { Console.WriteLine("无参的情况"); };
- Action<int> action = (int i) => { Console.WriteLine(i.ToString()); };
- action += (int i) => { Console.WriteLine((i+1).ToString()); };
- if (action != null)
- action(1);
Func 是有返回参数, 所以格式是 Fun<T t1,.....,TResult result>, 这个返回类型为最后一个参数, 并且是必须的, 传入参数类型可以没有, 但是返回类型必须要.
- Func<string,string> func = (string str) => { return str; };
- if(func != null)
- Console.WriteLine(func("hahaha"));
二, 委托的应用
委托的声明和使用其实不难, 但是很大的一个困扰就是不知道什么时候使用, 园中有很多委托的文章我看了很多, 大家都举了很多的例子, 比如烧开水啥的, 但是我觉得还是用实际的例子来说明比较好. 可能很多人在工作了几年后都没有用过委托. 所以, 实践出真知. 只有当真正用到的时候才能明白其中的含义.
这里举在窗体程序中的跨窗体调用方法的例子. 现在有两个窗体 Form1 和 Form2, 分别由一个文本框和一个按钮. 现在通过 Form1 中的按钮打开 Form2 窗体, 然后通过 Form2 窗体中的按钮点击更新 Form1 中文本框的内容.
Form1 窗体的代码如下:
- private void OpenBtn_Click(object sender, EventArgs e)
- {
- Form2 form2 = new Form2();
- form2.action = this.ModifyName;
- form2.Show();
- }
- private void ModifyName(string name)
- {
- NameTxt.Text = name;
- }
Form2 窗体的代码如下:
- public Action<string> action;
- private void ModifyBtn_Click(object sender, EventArgs e)
- {
- ConfirmModify(action);
- }
- private void ConfirmModify(Action<string> action)
- {
- // 省略数据库修改等操作.......
- if (action != null)
- action(ModifyTxt.Text);
- }
代码非常简单, Form1 和 Form2 中的方法都是私有的, 只通过 Form2 中的委托对象来执行 Form1 中的私有方法. 虽然还有别的方法可以实现这个功能, 但是我觉得这个方案是比较好的.
三, 总结
有问题可以留言讨论, 希望可以给大家带来帮助, 共勉!
来源: https://www.cnblogs.com/xwc1996/p/10268725.html