委托学习过程及委托、Lambda表达式和匿名方法的关系总结及事件总结
第一章,当开始学习委托的时候,我们会问什么是委托?为什么要学习委托?
一,什么是委托?
委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-Else(Switch)语句,同时使得程序具有更好的可扩展性。
(一个委托是一个指向一个方法的引用,或者说,一个委托的实例就是一个指向某个方法的对象)。
二,为什么要学习委托?
1,通常情况下:当项目中所需的功能(需求)越多,则相应需要的方法也就越多,一般做法是每个功能(需求)单独学方法,但是这样会造成代码冗余。
例如:三个需求
//1、将一个字符串数组中每个元素都转换成大写
//2、将一个字符串数组中每个元素都转换成小写
//3、将一个字符串数组中每个元素两边都加上 双引号
class Program { static void Main(string[] args) { //三个需求 //1、将一个字符串数组中每个元素都转换成大写 //2、将一个字符串数组中每个元素都转换成小写 //3、将一个字符串数组中每个元素两边都加上 双引号 string[] words = { "abCDefG", "HIJKlmnOP", "QRsTuvW", "XyZ" };
////////////////普通方法调用函数//////// ProStToUpper(words); ProStrToLower(words); ProStrSYH(words); }
//三个具体的方法 的定义 public static void ProStToUpper(string[] word) { for (int i = 0; i < word.Length; i++) { word[i] = word[i].ToUpper(); } } public static void ProStrToLower(string[] word) { for (int i = 0; i < word.Length; i++) { word[i] = word[i].ToLower(); } } public static void ProStrSYH(string[] word) { for (int i = 0; i < word.Length; i++) { word[i] = "\"" + word[i] + "\""; } } }
2,有没有一种好的方法可以将代码中的一些方法提出来并将方法作为参数当需要时作为参数传递实现功能,即将方法作为参数???? 因此需要某种类型的形参来接受作为参数的方法的传递,即有一个参数可以接受传递的方法参数。??? 这种类型为委托类型,传递的方法为委托方法。
public delegate string DelProStr(string words); //////////////////////////////////////////////(1)声明委托 (在类的外部声明 返回值类型+参数类型及个数) class Program { static void Main(string[] args) { //三个需求 //1、将一个字符串数组中每个元素都转换成大写 //2、将一个字符串数组中每个元素都转换成小写 //3、将一个字符串数组中每个元素两边都加上 双引号 string[] words = { "abCDefG", "HIJKlmnOP", "QRsTuvW", "XyZ" }; //1. ProStr(words, StrToUpper); //2. ProStr(words, StrToLower); //3 ProStr(words, StrSYH);//传参 方法(StrSYH)作为参数 (3)将委托与具体方法绑定 for (int i = 0; i < words.Length; i++) { Console.WriteLine(words[i]); } Console.ReadKey(); } public static void ProStr(string[] word, DelProStr del)
//传参时 DelProStr del=StrToUpper或(StrToLower)或(StrSYH):《=》 DelProStr del= new DelProStr(具体方法);委托所指向的函数必须跟委托具有相同的签名:相同的返回值类型+参数类型及个数 { for (int i = 0; i < word.Length; i++) { word[i] = del(word[i]); ///////////////(4)委托调用 } } /////////////////////////////////////////////////////////////////////////////////////////(2)根据委托定义具体方法///////// public static string StrToUpper(string word)//具体方法1 { return word.ToUpper(); } public static string StrToLower(string word)//具体方法2 { return word.ToLower(); } public static string StrSYH(string word)//具体方法3 { return "\"" + word+ "\""; } }
从上面可以看出其实,代码还是不少,继续改进为: (使用匿名函数) 方法只执行一次是时考虑使用 (匿名函数当做参数传递)
public delegate string DelProStr(string word); //DelProStr 委托名称 class Program { static void Main(string[] args) { //三个需求 //1、将一个字符串数组中每个元素都转换成大写 //2、将一个字符串数组中每个元素都转换成小写 //3、将一个字符串数组中每个元素两边都加上 双引号 string[] words = { "abCDefG", "HIJKlmnOP", "QRsTuvW", "XyZ" }; ProStr(words, delegate (string word) { // return "\"" + word + "\""; //return word.ToUpper();//大写 return word.ToLower();//小写 }); for (int i = 0; i < words.Length; i++) { Console.WriteLine(words[i]); } Console.ReadKey(); } public static void ProStr(string[] word, DelProStr del)//del 委托变量 { for (int i = 0; i < word.Length; i++) { word[i] = del(word[i]);//委托调用 } } }
现在我们可能会产生一种疑问匿名函数是啥?该咋用?
1.匿名函数概念?
简而言之,匿名函数就是没有函数名称的函数(方法)。
2.该咋用?
A,B函数需要在满足某种特定条件下才去执行,因此我们不必要去为功能单独添加函数A,B,我们可以使用匿名函数来直接实现 。
使用格式: 委托变量=delegate(参数){需要执行的A,B方法体} 参数:是根据委托创建的具体方法需要的参数类型
还有一种与匿名函数相似的方法,但是比匿名函数高级的写法为lambda表达式 :没有函数名称与delegate的函数
public delegate string DelProStr(string word); class Program { static void Main(string[] args) { //三个需求 //1、将一个字符串数组中每个元素都转换成大写 //2、将一个字符串数组中每个元素都转换成小写 //3、将一个字符串数组中每个元素两边都加上 双引号 string[] words = { "abCDefG", "HIJKlmnOP", "QRsTuvW", "XyZ" }; ProStr(words, (string word)=> { return word.ToLower();//小写 });//使用lambda表达式的写法
for (int i = 0; i < words.Length; i++) { Console.WriteLine(words[i]); } Console.ReadKey(); } public static void ProStr(string[] word, DelProStr del) { for (int i = 0; i < word.Length; i++) { word[i] = del(word[i]); } } }
“Lambda 表达式”是一个匿名函数,
所有 Lambda 表达式都使用 Lambda 运算符 =>,该运算符读为“goes to”。该 Lambda 运算符的左边是输入参数(如果有),右边包含表达式或语句块。Lambda 表达式 x => x * x 读作“x goes to x times x”。
第二章,通过另外的例子总结委托、Lambda表达式和匿名方法的关系。
1.委托是一种引用方法的类型。一旦为委托分配了方法,委托将与该方法具有完全相同的行为。委托方法的调用可以像其他任何方法一样,具有参数和返回值
2.C# 2.0 版引入了 匿名方法的概念,此类方法允许将代码块作为参数传递,以代替单独定义的方法。C# 3.0 引入了 Lambda 表达式,利用它们可以更简练地编写内联代码块。
匿名方法和 Lambda 表达式(在某些上下文中)都可编译为委托类型。这些功能统称为匿名函数。
“匿名方法”就是没有名称的方法。匿名方法通常用于将代码块作为委托参数进行传递。
3.“Lambda 表达式”是一个匿名函数,它可以包含表达式和语句,并且可用于创建委托或表达式目录树类型。
所有 Lambda 表达式都使用 Lambda 运算符 =>,该运算符读为“goes to”。该 Lambda 运算符的左边是输入参数(如果有),
右边包含表达式或语句块。Lambda 表达式 x => x * x 读作“x goes to x times x”。
例如:使用三种方式实现两个数的相加。
delegate int AddDelegate(int a, int b);//委托类 class AddClass { int a; int b; public AddClass(int a, int b)//构造函数 { this.a = a; this.b = b; } ///////////////////////////////////三个具体的方法 private int Add1(int a, int b) { return a + b; } public int Add2() { AddDelegate myAdd = new AddDelegate(Add1);//使用new 关键字创建对象必须要先有方法与之绑定 //<=> AddDelegate myAdd=Add1 return myAdd(a, b); } public int Addition3() { AddDelegate myAdd = delegate(int a, int b) { return a + b; }; //匿名方法 return myAdd(a, b); } public int Addition3() { AddDelegate myAdd = (a, b) => a + b; //Lambda表达式 return myAdd(a, b); } }
特例:
(1)Func委托 是系统已经定义好的委托 0~16个参数,有返回值
(1.1)无参数带返回值 Func<T> fun=()=>{方法体带返回值}
(1.2)有参数带返回值 Func<T,TResult> fun=()=>{方法体带返回值} TResult为方法的返回类型。
Func委托有5个重载形式,区别仅在于它所指向的方法的签名的参数个数,分别如下: Func<TResult> Func<T,TResult> Func<T1,T2,TResult> unc<T1,T2,T3,TResult> Func<T1,T2,T3,T4,TResult> 其中T,T1,..T4是委托指向的方法的参数的类型,TResult为方法的返回类型。
如何使用Func委托?
首先,需要一个具体的方法。
其次,使用Func委托定义变量并关联方法
最后,委托调用
(2)Action委托 是系统已经定义好的委托 0~16个参数,无返回值
(2.1)Action action=(参数可选)=>{方法体中无返回值}
(2.2)Action<T> act=(参数可选)=>{方法体中无返回值}
Action委托也有16个重载形式,分别如下: Action Action<T> Action<T1,T2> Action<T1,T2,T3> .. Action<T1,T2,T3,T4..> 其中T,T1,..T4...是委托指向的方法的参数的类型。 从上面的委托形式我们就可以分析出来,Func和Action委托的唯一区别在于Func要有返回值, Action没有返回值。
第三章: 事件
1.事件的由来:
实际应用中,通常都是 Program在一个类中,三个具体方法在另外的类中。但并不是所有的字段都应该声明成public,合适的做法是应该public的时候public,应该private的时候private。对于委托对象有些时候需要public有些时候需要private。当把委托对象(DelProStr)声明为private时,会怎么样呢???有点小无语。因为声明委托的目的就是为了把它暴露在类的客户端进行方法的注册,你把它声明为private了,客户端对它根本就不可见,那它还有什么用?
再看看把委托对象(DelProStr)声明为 public 会怎样?结果就是:在客户端可以对它进行随意的赋值等操作,严重破坏对象的封装性。
现在我们想想,如果DelProStr不是一个委托类型,而是一个string类型,你会怎么做?答案是使用属性对字段进行封装。于是,Event出场了,它封装了委托类型的变量,使得:在类的内部,不管你声明它是public还是protected,它总是private的。在类的外部,注册“+=”和注销“-=”的访问限定符与你在声明事件时使用的访问符相同。
2.什么是事件?
事件其实没什么不好理解的,声明一个事件不过类似于声明一个进行了封装的委托类型的变量而已。
public DelProStr delProStr;//delProStr这是一个委托变量
public event DelProStr eventDelProStr;//eventDelProStr 事件:委托变量关键字+event
委托:是一种类型 (形如Person类)
事件:是委托的一个实例 (形如 由Person类实例化后的对象变量man) Person man=new Person();
3.怎么使用事件??
前提是要声明一个委托
首先,声明一个事件(在委托的基础上才能实现) :实为对委托的封装
然后判断eventDelProStr是否为空 :只有在(动作)事件注册后并判断是否有事件然后才能执行。
if(eventDelProStr!=null)
{
eventDelProStr.Invoke();//执行注册的(动作) 而不是方法
}
最后,(外界)注册(动作)事件 (通过“+=”和 “-+”) (跟委托与方法关联的步骤一样)
委托与事件的区别:
(1)事件不允许外面直接对事件赋值方法 委托在外界赋值后会导致赋值之前的注册会失效
(2)事件不允许在外面直接调用事件 委托在外界调用会跳过委托方法(会重置关联的方法使之变为null)
学习地址:http://www.tracefact.net/CSharp-Programming/Delegates-and-Events-in-CSharp.aspx (C# 中的委托和事件)