CSharp中委托(一)委托、匿名函数、lambda表达式、多播委托、窗体传值、泛型委托
委托就是一种类型:一种表示标明函数de类型的类。
因此,可将函数作为参数变量传来传去。
而将函数作为参数传递给委托类型的变量,相当于将函数看做参数传入函数中。
符号:=>:就是将参数传递给函数(匿名的话就是:函数体)
定义一个委托,相当于定义了一个类。因此写在类外面。
声明委托类型的类时,这个类,要跟被委托的函数有相同的签名(参数和返回值)。
要用这个委托类型的类去声明变量,以此变量用来传递函数。
被委托的函数:将要作为参数传递的函数。
委托
先来个例子:
三个需求:
1、将一个字符串数组中每个元素都转换成大写
2、将一个字符串数组中每个元素都转换成小写
3、将一个字符串数组中每个元素两边都加上 双引号
string[] names = { "abCDefG", "HIJKlmnOP", "QRsTuvW", "XyZ" };
//StrToLower(names);
StrToUpper(names);
//StrSYH(names);for (int i = 0; i < names.Length; i++)
{Console.WriteLine(names[i]);
}
用三个方法分别去实现。
public static void StrToUpper(string[] name)
{for (int i = 0; i < name.Length; i++){name[i] = name[i].ToUpper();}
}public static void StrToLower(string[] name)
{for (int i = 0; i < name.Length; i++){name[i] = name[i].ToLower();}
}public static void StrSYH(string[] name)
{for (int i = 0; i < name.Length; i++){name[i] = "\"" + name[i] + "\"";}
}
三个方法有大量相同重复代码,只有少部分是不同的。而且写完方法,三个功能要用三个函数,我们希望一个函数搞定。
首先解决用一个函数来实现这三个功能,将下面的三个函数名作为参数,来输入到这个函数中。用哪个函数名,就实现哪个函数的功能。
那么将函数名作为实参传入函数的这种操作,我们称为委托。
实质:将函数名作为参数传入函数中。将原函数要实现的功能,委托给一个新函数来实现。函数指针。指向函数。
函数名作为一个值传递给一个变量,这个变量必定有一个类型,这个类型就是我们提到的委托类型:新的类型关键字:delegate。
声明一个委托类型,委托所指向(非常类似指针)的函数,必须跟委托具有相同的签名(参数和返回值)。
(C/C++中,指针指向的变量类型相同,int*类型的指针指向int类型变量,char*类型的指针指向char类型的变量)
C语言中:函数指针就是指向函数的指针。
这个委托可以看做是一个指向函数的指针类型。
回到我们的需求案例中,如上,函数没有返回值,但是有一个string[]类型的输入。委托写的位置是:类外,命名空间里面。因此声明委托如下:
public delegate void DelStrTo(string[] name);
写一个方法,将需要委托的函数作为参数传入,相当于传递一个方法过来。
这个参数的类型为刚才声明的委托类型:即:DelStrTo类型。
标明函数这个参数位置需要传入一个什么类型(函数签名相同)的函数方法:
public static void Test(string[] name, DelStrTo del)
{del(name); //委托需要一个参数,写入name。
}
本来这个方法是要调用上面那三个函数来实现对应的功能。为了传递这三个方法,需要使用委托类型的参数。委托类型的参数里面装(赋值)的就是三个函数中的其中一个。
接下来,我们调用Test方法。
委托的第一种使用方法:声明委托指向函数。
string[] names = { "abCDefG", "HIJKlmnOP", "QRsTuvW", "XyZ" };
//StrToLower(names);
//StrToUpper(names);
//StrSYH(names);//DelStrTo del = new DelStrTo(StrToUpper); // 创建一个委托对象,它指向了一个函数。类似于Thread用法,传入一个函数。
//DelStrTo del = new DelStrTo(StrToLower);
DelStrTo del = new DelStrTo(StrSYH);
del(names);for (int i = 0; i < names.Length; i++)
{Console.WriteLine(names[i]);
}
委托的第二种写法:直接把方法赋值给一个委托类型。
string[] names = { "abCDefG", "HIJKlmnOP", "QRsTuvW", "XyZ" };
//StrToLower(names);
//StrToUpper(names);
//StrSYH(names);//DelStrTo del = new DelStrTo(StrToUpper); // 创建一个委托对象,它指向了一个函数。类似于Thread用法,传入一个函数。
//DelStrTo del = new DelStrTo(StrToLower);
//DelStrTo del = new DelStrTo(StrSYH);
DelStrTo del = StrToUpper; // 方法直接赋值给委托类型。
DelStrTo del = StrToLower;
DelStrTo del = StrSYH;del(names);for (int i = 0; i < names.Length; i++)
{Console.WriteLine(names[i]);
}
委托的第三种写法:既然可以赋值,那么就可以作为参数传递。
把一个方法作为参数,传递给另一个方法,那么参数应该有类型。方法作为参数的类型就是委托类型。
把函数赋值给一个委托类型,前提是这个委托的签名跟函数相同。
static void Main(string[] args)
{//三个需求//1、将一个字符串数组中每个元素都转换成大写//2、将一个字符串数组中每个元素都转换成小写//3、将一个字符串数组中每个元素两边都加上 双引号string[] names = { "abCDefG", "HIJKlmnOP", "QRsTuvW", "XyZ" };//StrToLower(names);//StrToUpper(names);//StrSYH(names);//DelStrTo del = new DelStrTo(StrToUpper); // 创建一个委托对象,它指向了一个函数。类似于Thread用法,传入一个函数。//DelStrTo del = new DelStrTo(StrToLower);//DelStrTo del = new DelStrTo(StrSYH);//DelStrTo del = StrToUpper; // 直接把方法赋值给委托。//DelStrTo del = StrToLower;//DelStrTo del = StrSYH;//del(names);Test(names, StrToUpper);Test(names, StrToLower);Test(names, StrSYH);for (int i = 0; i < names.Length; i++){Console.WriteLine(names[i]);}}
上面说了半天不同写法。绕来绕去,还是不如最初的写法,直接调用方法多好。为此我们需要一点新的东西。
匿名函数
对于Test函数我们重新写一个:将上面三个函数共同的代码提取出来。
public static void Test(string[] names, DelStrTo del)
{//del(name); //委托需要一个参数,写入name。for (int i = 0; i < names.Length; i++){names[i] = del(names[i]);}
}
我们希望用委托将三个需求功能实现代码中不同的部分,委托掉。但是编译器此时会提醒。del无法从“string”转换为“string[]”。因此考虑字符串数组中,每个元素都是字符串,我们可以把委托输入的参数写为字符串string,而不是原来的字符串数组string[]。而且我们test方法已经遍历了,原来的方法只是处理每个元素,因此委托输入的参数应该为string类型,而不是string[]类型。并且有返回值string,而不是void。
public delegate string DelStrTo(string name);
此时洋气的写法有了。我们用匿名函数,也就是delegate里面以没有命名的方式实现了一个函数的方法。既有委托,又包含不同的实现方法。代码量大大减少。公共部分包含在test中,差异部分用匿名函数来简化。
public delegate string DelStrTo(string name);class Program
{static void Main(string[] args){//三个需求//1、将一个字符串数组中每个元素都转换成大写//2、将一个字符串数组中每个元素都转换成小写//3、将一个字符串数组中每个元素两边都加上 双引号string[] names = { "abCDefG", "HIJKlmnOP", "QRsTuvW", "XyZ" };Test(names, delegate (string name) { return name.ToUpper(); });Test(names, delegate (string name) { return name.ToLower(); });Test(names, delegate (string name) { return "\"" + name + "\""; });for (int i = 0; i < names.Length; i++){Console.WriteLine(names[i]);}}public static void Test(string[] name, DelStrTo del){//del(name); //委托需要一个参数,写入name。for (int i = 0; i < name.Length; i++){name[i] = del(name[i]);}}}
当方法只写一次,只用一次的时候,匿名函数是非常省力的方式。
嵌套写在里面可能有点复杂,我们看一个简单的例子。
我们考虑一个问题,Test函数还有用吗?
Test函数只是帮我们传递了一个委托,我们不用Test来传来传去了。Test的功能我们也直接用匿名函数来实现。。
DelStrTo del = delegate (string[] names)
{for (int i = 0; i < names.Length; i++){names[i] = names[i].ToUpper();}
};
del(names);
需要函数干的事情,交给委托,然后匿名就好了。委托和匿名函数的签名也必须一致。如果委托是string类型的,那么匿名函数,就需要有return。而此处我们只转换。
lambda表达式
还有一个洋气的写法:lambda表达式:匿名函数更为简写的形式。
static void Main(string[] args)
{//三个需求//1、将一个字符串数组中每个元素都转换成大写//2、将一个字符串数组中每个元素都转换成小写//3、将一个字符串数组中每个元素两边都加上 双引号string[] names = { "abCDefG", "HIJKlmnOP", "QRsTuvW", "XyZ" };//DelStrTo del = delegate (string[] names)//{// for (int i = 0; i < names.Length; i++)// {// names[i] = names[i].ToUpper();// }//};DelStrTo del = (string[] names) =>{for (int i = 0; i < names.Length; i++){names[i] = names[i].ToUpper();}};del(names);for (int i = 0; i < names.Length; i++){Console.WriteLine(names[i]);}}
lambda表达式中如果没有参数,可以将参数省去,但是括号不能省略。
符号=>表示goes to:将参数传向方法体。
看几个lambda表达式的例子。
namespace _12_lambda表达式
{public delegate void DelOne();public delegate void DelTwo(string name);public delegate string DelThree(string name);class Program{static void Main(string[] args){DelOne del = () => { // dosomething,比如打印申明 };// delegate() { };DelTwo del2 = (string name) => { };//delegate(string name) { };DelThree del3 = (string name) => { return name; };//delegate(string name) { return name; };List<int> list = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9 };list.RemoveAll(n => n > 4); //条件筛选器。foreach (var item in list){Console.WriteLine(item);}Console.ReadKey();}}
}
多播委托
委托不仅可以指向一个函数,还可以指向多个函数。
public delegate void DelTest();
class Program
{static void Main(string[] args){DelTest del = T1;del += T2;del += T3;del += T4;del -= T3;del -= T1;del();Console.ReadKey();}public static void T1(){Console.WriteLine("我是T1");}public static void T2(){Console.WriteLine("我是T2");}public static void T3(){Console.WriteLine("我是T3");}public static void T4(){Console.WriteLine("我是T4");}
}
窗体传值
场景表述:
窗体1有一个按钮,一个label标签。点击按钮就可以打开窗体2。
窗体2有一个按钮,一个Txtbox文本框。点按钮就可以将窗体2中文本框的文本显示到窗体1中的标签label中。
一个场景有方法,没数据;另一个窗口有数据,没方法。
private void btnfm1_Click(object sender, EventArgs e)
{Form2 fm = new Form2();fm.Show();
}
为了实现文本消息的传递,我们在窗体1中类内,将值传递给lable1,我们写一个方法:
public void ShowMsg(string str)
{label1.Text = str;
}
这个str其实就是form2中的文本框的内容消息。
分析一下,目前的境地:窗体1也就是说Form1类中有方法把值赋值给label1,也就是说显示,但是他没有值(有方法无数据)。窗体2也就是说Form2类中有要传递的值(有数据无方法),但是没有传递的方法,方法在窗体1中Form1类中。
我们需要在Form2类中拿到Form1类中的方法就好了。谁可以把方法给传递过去。引出了委托。
思路:
- 在Form2中使用Form1中的方法,所以这个方法作为参数从Form1中传入。
- 这个委托变量在Form1中赋值,然后在Form2中真正被调用。
- 如何实现传递,用委托。在哪里传递的。可以在Form1中构造Form2时,将方法从Form1传递给Form2。
- 也就是在Form2构造函数时,需要Form1传递一个方法过去。
- 所以委托在Form2的构造函数中作为一个参数占位,同时,将委托声明在Form2模块中。
声明一个委托,委托最终要指向我们Form1中的ShowMsg函数。被指向的函数签名:无返回值,有参数string。在Form2类中:
public delegate void DelShowMsg(string str);
怎么把方法传递过来?
方法在Form1中,委托在Form2中。怎么从Form1传递到Form2中。我们回看代码:在form1中,我们写了一个点击按钮初始化form2的方法,如果我们在初始化form2时可以把委托传进去就好了。构造函数可以做这事。new Form2(ShowMsg)。;这样Form2中既有方法又有值了。就能完成跨窗体传值这事。
(大数据思维:算法找数据。)
为此我们需要重写Form2中的构造函数。在form2的构造函数中也需要一个委托类型的字段来接收传递过来的委托函数。Form2中,我们声明一个委托类型的字段。
public DelShowMsg _del;
传递过来的值赋值给字段。
namespace _02窗体传值
{public delegate void DelShowMsg(string str);public partial class Form2 : Form{public DelShowMsg _del;// 构造函数:构造Form2窗体时,把要在窗体中执行显示的函数方法传递过来。算法找数据。// Form2创建时,需要一个方法传递过来。所以构造函数前要有一个委托。// Form2中应该有个字段,接受这个委托方法,便于后面实现。public Form2(DelShowMsg del){this._del = del;InitializeComponent();}}
}
通过委托把函数传递过来了,我们怎么用?
窗体2拿到了方法,当点击发送按钮之后,就应该把值传递给Form1。form2中可以访问到form1中的这个方法,这个方法就在_del中。直接执行就好了。
Form2中的字段_del,就是Form1派过去的关于方法ShowMsg的代表。_del在Form2中行使Form1中ShowMsg的权力,执行其功能。
private void btnfm2_Click(object sender, EventArgs e)
{_del(textBox1.Text);
}
form1中初始化Form2时,传递函数方法ShowMsg。
全码如下:
namespace _02窗体传值
{public partial class Form1 : Form{public Form1(){InitializeComponent();}private void btnfm1_Click(object sender, EventArgs e){Form2 fm = new Form2(ShowMsg);fm.Show();}public void ShowMsg(string str){label1.Text = str;}}
}
namespace _02窗体传值
{public delegate void DelShowMsg(string str);public partial class Form2 : Form{public DelShowMsg _del;// 构造函数:构造Form2窗体时,把要在窗体中执行显示的函数方法传递过来。算法找数据。// Form2创建时,需要一个方法传递过来。所以构造函数前要有一个委托。// Form2中应该有个字段,接受这个委托方法,便于后面实现。public Form2(DelShowMsg del){this._del = del;InitializeComponent();}private void btnfm2_Click(object sender, EventArgs e){_del(textBox1.Text);}}
}
泛型委托
场景:获取任意数组的最大值。
核心代码:if(max<names[i]){max = name[i]}也就是说max减去names[i]小于0。则赋值。比较函数委托。
namespace _11_泛型委托
{public delegate int DelCompare<T>(T t1, T t2);// public delegate int DelCompare(object o1, object o2);class Program{static void Main(string[] args){int[] nums = { 1, 2, 3, 4, 5 };int max = GetMax<int>(nums, Compare1);Console.WriteLine(max);string[] names = { "abcdefg", "fdsfds", "fdsfdsfdsfdsfdsfdsfdsfsd" };string max1 = GetMax<string>(names, (string s1, string s2) =>{return s1.Length - s2.Length;});Console.WriteLine(max1);Console.ReadKey();}public static T GetMax<T>(T[] nums, DelCompare<T> del){T max = nums[0];for (int i = 0; i < nums.Length; i++){//要传一个比较的方法if (del(max, nums[i]) < 0){max = nums[i];}}return max;}public static int Compare1(int n1, int n2){return n1 - n2;}}
}
所谓的泛型:就是<T>。
CSharp中委托(一)委托、匿名函数、lambda表达式、多播委托、窗体传值、泛型委托相关推荐
- C++11新特性中的匿名函数Lambda表达式的汇编实现分析(二)
2019独角兽企业重金招聘Python工程师标准>>> C++11新特性中的匿名函数Lambda表达式的汇编实现分析(一) 首先,让我们来看看以&方式进行变量捕获,同样没有参 ...
- 【Kotlin】Kotlin 函数总结 ( 具名函数 | 匿名函数 | Lambda 表达式 | 闭包 | 内联函数 | 函数引用 )
文章目录 一.函数头声明 二.函数参数 1.默认参数值 2.具名参数 三.Unit 函数 四.TODO 函数抛出异常返回 Nothing 类型 五.反引号函数名 六.匿名函数 七.匿名函数的函数类型 ...
- 14_python基础—匿名函数lambda表达式
文章目录 一.lambda 表达式 1.1 lambda的应用场景 1.2 lambda语法 快速入门 1.3 示例:计算a + b 1.3.1 函数实现 1.3.2 lambda实现 1.4 lam ...
- python匿名函数的作用_Python匿名函数 Lambda表达式作用
在Python这门优美的编程语言中,支持一种有趣的语法格式(表达式),可以让我们在单行内创建一个最小的函数-python lambda匿名函数. 据说是借鉴了Lisp语言中lambda表达式,它可以使 ...
- Scala匿名函数Lambda表达式详解
1 前言 大家好,我是楚生辉,在未来的日子里我们一起来学习大数据相关的技术,一起努力奋斗,遇见更好的自己! 本文详细的介绍了scala的匿名函数,有需要的小伙伴可以学习~ Scala 中定义匿名函数的 ...
- Python匿名函数——lambda表达式
如果要定义的函数很简单,一个return语句就能搞定,可以使用lambda表达式来定义, lambda表达式的语法如下: lambda parameters: expression lambda表达式 ...
- C++ [](){} 匿名函数 lambda表达式
C++ [](){}_龍的传人-CSDN博客_[]() c++ lambda函数能够捕获lambda函数外的具有自动存储时期的变量.函数体与这些变量的集合合起来叫闭包.lambda函数能够捕获lamb ...
- 用python计算1~100的阶乘之和_在Python中递归函数调用举例and匿名函数lambda求1~100的和及计算阶乘举例...
1.递归列出目录里的文件的脚本举例 列出目录中的文件可以通过下面方法:os.listdir() In [1]: import os In [4]: os.listdir('/root') Out[4] ...
- 一木.溪桥学Python-09:函数的返回值、函数的作用域、作用域的优先级、递归函数、内置文件中常用方法、匿名函数lambda、高阶函数
一木.溪桥 在Logic Education跟Amy学Python 12期:Python基础课 一木.溪桥学Python-09:函数的返回值.作用域.作用域的优先级.递归函数.内置文件中常用方法.匿名 ...
- python函数+定义+调用+多返回值+匿名函数+lambda+高级函数(reduce、map、filter)
python函数+定义+调用+多返回值+匿名函数+lambda+高级函数(reduce.map.filter) Python 中函数的应用非常广泛,比如 input() .print().range( ...
最新文章
- 树莓派应用实例3:环境光照强度测量
- Linux socket关闭连接shutdown与close
- 线程池,封装使用,实现控制子线程
- 软件分类:自由软件、开放源代码软件、公共软件、私有软件、版权所无软件...
- WinAPI: waveOutGetNumDevs - 获取波形输出设备的数目
- 12-6路径的其他操作
- 录制高清微课视频,录屏软件有什么推荐?
- VTK笔记-vtkProbeFilter探针类
- R语言基础画图/绘图/作图
- Less入门以及一些前端面试题
- 手机查看企业qq邮件服务器,QQ企业邮箱怎么用?手机QQ邮箱收发邮件的方法
- 英语语法快速入门3--名词性从句(附思维导图)
- 细胞器基因组|比较基因组分析助力深度挖掘细胞器进化关系
- 【BZOJ1818】内部白点
- 荣耀play4tpro有没有鸿蒙,荣耀play4tpro有nfc吗?没有 只能借助支付宝等
- python编程midi键盘按键错乱_键盘按键错乱超简单解决教程
- scrapy-splash java,小白程序员-运用Scrapy-splash爬取动态js页面
- websocket封装,有心跳和断开重联功能
- oracle voting disk 大小,2.Oracle Voting Disk 管理
- CEO是世界上最孤独的人
热门文章
- Python电子书教程汇总
- MATLAB深度学习layer、options参数浅析与文章推荐
- 2.Modbus通信协议-软件调试4个工具软件(推荐)
- 软件推荐——Modbus调试工具Modbus Poll与Modbus Slave
- sb3转换exe_SB转exe或swf(sb3toSB2.rar
- [Gdiplus]_[初级]_[使用插值模式控制图片的缩放质量]
- XShell6免费版获取(个人版)
- javaMail发送邮件设置发件人中文昵称
- ISIS 7 Professional运行报错解决方案
- 防止百度网盘和谐/暗中观察我的资源