7.1 委托和事件(1)
委托和事件一直被视为C#的难点,.NET的面试题中经常出现这些知识点。本节通过常见的面试考题辅以典型实例剖析,对知识点深度讲解。
面试例题1:举例说明如何使用C#中的委托?
考点:委托的含义,委托和引用方法的联系。
出现频率:★★★★★
解答
本实例通过用户选择不同格式显示输入的文字,解决办法是首先建立委托类型,其签名可以匹配多个格式化字符,然后在Main()方法中创建委托对象,通过判断用户的输入,将委托对象进行相应地初始化,从而完成不同方法的引用。在目录下新建一个程序文件,并命名为DelegateTest.cs,编写代码如代码7.1所示。
代码7.1 简单使用C#的委托:DelegateTest.cs
using System; namespace NET.CHP6 { public class DelegateTest { //定义一个MyDel委托类型,其签名接收两个string类型参数,返回类型也为string public delegate string MyDel(string nm, string pwd); static void Main(string[] args) { //声明MyDel类型的md变量 MyDel md; Console.WriteLine("请输入2个参数,并用逗号分隔"); //将用户输入值赋值给InputValue变量 string InputValue = Console.ReadLine(); //获取英文逗号在InputValue变量中的索引值,并赋值给Pos变量 int Pos = InputValue.IndexOf(","); //将英文逗号前面部分的子字符串赋值给ValueA变量 string ValueA = InputValue.Substring(0, Pos); //将英文逗号后面部分的子字符串赋值给ValueB变量 string ValueB = InputValue.Substring(Pos + 1); Console.WriteLine("请输入A,B,C,D,以确定所调用方法"); //将用户输入值转换为大写,并赋值给InputMethod方法 string InputMethod = Console.ReadLine().ToUpper(); //判断InputMethod变量的值 switch (InputMethod) { case "A": //如果用户输入为A或a,则创建MyDel委托对象, 其引用变量为md,指向formatA方法 md = new MyDel(formatA); break; case "B": //如果用户输入为B或b,则创建MyDel委托对象, 其引用变量为md,指向formatB方法 md = new MyDel(formatB); break; case "C": //如果用户输入为C或c,则创建MyDel委托对象, 其引用变量为md,指向Another类的formatC静态方法 md = new MyDel(Another.formatC); break; case "D": //如果用户输入为D或d,则首先创建创建Another对象,引用为ano变量 //然后创建MyDel委托对象,其引用变量为md,指向ano对象的formatD方法 Another ano = new Another(); md = new MyDel(ano.formatD); break; default: //其他情况下,创建MyDel委托对象,其引用变量为md,指向formatA方法 md = new MyDel(formatA); break; } //调用md委托对象,将返回值赋值给result变量 string result = md(ValueA, ValueB); Console.WriteLine("\n\t==========以下为委托方法执行结果=========="); //输出result变量值 Console.WriteLine(result); } //定义两个静态方法formatA和formatB,符合MyDel委托类型的签名 static string formatA(string a, string b) { string words = "I am the first static method. \nMy name is " + a + "\nMy password is " + b; words += "\n我是第一个静态方法。\n我的名字是: " + a + "\n我的密码是: " + b; return words; } static string formatB(string a, string b) { string words = "I am the second static method .\nMy name is " + a + "\nMy password is " + b; words += "\n我是第二个静态方法。\n我的名字是: " + a + "\n我的密码是: " + b; return words; } } //定义外部类,其内含静态方法和实例方法符合MyDel委托类型签名 class Another { internal static string formatC(string a, string b) { string words = "I am the third static method, I belong to Another class.\nMy name is " + a + "\nMy password is " + b; words += "\n我是第三个静态方法,我属于 Another 类。 \n我的名字是: " + a + "\n我的密码是: " + b; return words; } internal string formatD(string a, string b) { string words = "\n我是1个实例方法,我所属的对象实 例是 Another 类所构造的。\n我的名字是: " + a + "\n我的密码是: " + b; return words; } } }
|
在Main()方法以外定义了1个委托类型,其名称为MyDel,其返回值为String类型,并接收2个String类型的参数。另外还声明了2个静态方法和1个类,其中Another类含有2个方法,分别为静态方法formatC()和实例方法formatD(),这2个方法都用internal修饰。
在Main()方法中,首先声明MyDel委托类型的对象,并命名为md。通过Console.ReadLine()方法,接收用户的输入并存储到InputValue变量中,通过","将InputValue分隔为2个字符串,分别存储于ValueA和ValueB;然后接收用户的输入并存储于InputMethod变量中,通过switch语句判定用户输入是否为A、B、C、D或其他。不同的InputMethod值通过以下代码语句初始化md的引用:
最后直接输出md的方法调用。在命令行下编译DelegateTest.cs,执行DelegateTest程序,程序提示"请输入两个参数,并用逗号分隔"。输入相应的值后,程序再次提示"请输入A,B,C,D,以确定所调用方法",输入"abcd",即4个选择以外的字母,程序运行结果如图7.1所示。
|
(点击查看大图)图7.1 委托执行结果 |
由上可知,程序中的switch分支语句执行了default条件下的代码,即调用第1个静态方法。再次运行DelegateTest程序,程序提示调用方法时,输入"d",运行结果如图7.2所示。
如果首先输入"YeQing,password"字符串,然后输入"d",程序返回D字母对应的结果。由结果可知,委托类型对象md引用了Anothoer类的实例方法formatD()。
|
(点击查看大图)图7.2 委托执行结果 |
解析
很多C#书籍介绍过,C#对C++的借鉴过程中去掉了指针部分,但是在学习到C#委托类型的知识点时,很多C++的程序员会想起指针。不过C#中的委托并不完全等同于指针,相对于C++中的指针,委托是面向对象的、类型安全的类型,在编程中使用更加方便、可靠。
说明:CLR能够保证委托指向一个有效的方法,而不会指向无效地址或者越界地址。
可将委托看作一种新的对象类型,委托主要用于.NET Framework中的事件处理程序和回调函数。委托类型可定义一个签名,并且它只能持有与它的签名相匹配的方法引用。所有委托类型对象都有方法调用列表,它是在调用委托对象时所执行方法的一个链接列表。方法调用列表所链接的方法可以是静态方法,也可以是实例方法。对于实例方法,列表中所对应的是一个实例方法和其所属实例,而对于静态方法,列表中只对应一个方法。
说明:列表中的方法即为与委托类型定义签名相匹配的方法。
当委托类型匹配方法的返回类型和参数列表时,这个委托类型即和此方法兼容。另外,方法的名称等特性不需要和委托类型名称匹配。用delegate 关键字可以定义一个委托,通知编译器这是一个委托类型。委托类型声明的形式类似,没有函数体的函数,但是有相应的返回值和参数列表,其格式如以下代码所示:
public delegate 返回类型 委托名称(参数类型1 参数1,参数类型2 参数2,... );
|
通过委托定义中的返回值类型和参数列表即可匹配其他方法,而委托名称是这种委托类型的名称。delegate关键字定义时,临时定义了一个派生于System.MulticastDelegate类的密封类,这个类与其基类System.Delegate均同为委托提供必要的基础成员。在使用委托时声明该委托类型的对象,然后将这个对象初始化为该委托对象签名匹配的方法引用。其结果是该委托对象调用了相应的方法,这个委托对象也可当作这个方法使用。delegate关键字定义的委托类型名称是MyDel,创建该委托类型的对象,其对象被初始化后拥有方法调用列表,分析过程如图7.3所示。
|
(点击查看大图)图7.3 委托的分析图 |