=============================版权申明===============================
任何人都可以转载,但请注明转载地址和作者“天轰穿”,因为下面这是我正在写的 c# 这本书中的节选内容。
整本书都是这个风格,朋友们有啥期待尽管提。
===========================版权申明结束,内容开始======================
6.3.5 匿名方法
小天:我觉得多少还是有点死板。有不有什么办法可以让委托更灵活,比如某个阶段我并不确定具体要调用的被委托方法名。或者根本不想专门为某个委托就写一个冷僻的方法。
老田:可以使用匿名方法。在 2.0 之前的 C# 版本中,声明委托的唯一方法是使用命名方法。C# 2.0 引入了匿名方法,而在 C# 3.0 及更高版本中,Lambda 表达式取代了匿名方法,作为编写内联代码的首选方式。不过,本章讨论的这些内容也同样也适用于 Lambda 表达式(稍后讨论)。有一种情况下,匿名方法提供了 Lambda 表达式中所没有的功能。匿名方法使您能够省略参数列表,这意味着可以将匿名方法转换为带有各种签名的委托。这对于 Lambda 表达式来说是不可能的。
要将代码块传递为委托参数,创建匿名方法则是唯一的方法。下面新建一个Windows窗体应用程序,在Form1的设计界面上拖一个button,然后在窗体空白处点右键,查看代码,在Form1的构造函数中编写代码,为button1的click关联一个匿名方法作为执行体,具体Form1的构造函数代码如下:
public Form1() { InitializeComponent(); button1.Click += delegate(Object o, EventArgs e) { //匿名事件中的代码 MessageBox.Show("按钮事件!"); }; //注意大括号后面有一个分号作为结束 } |
然后运行程序。会发现,点击按钮执行的事件就是我们上面写的这个;
小天:匿名方法一定要跟上面示例中这两个参数吗?
老田:当然不是,上面示例之所以要跟这两个参数是因为button的click委托规定了接受他委托的方法的要求是这样的。否则我们也可以像下面这样:
//自定义一个委托类型(注意申明委托的位置,错了别怪我) delegate void Del(int x);
//申明一个Del委托类型的实例,并为他关联一个匿名类型的匿名事件 Del d = delegate(int k) { /* 方法体 */ }; |
名方法的优点是减少了要编写的代码。不必定义仅由委托使用的方法。在为事件定义委托时,这是非常显然的。这有助于降低代码的复杂性,尤其是定义了好几个事件时,代码会显得比较简单。使用匿名方法时,代码执行得不太快。编译器仍定义了一个方法,该方法只有一个自动指定的名称,我们不需要知道这个名称。
在使用匿名方法时,必须遵循两个规则。在匿名方法中不能使用跳转语句跳到该匿名方法的外部,反之亦然:匿名方法外部的跳转语句不能跳到该匿名方法的内部。
在匿名方法内部不能访问不安全的代码。另外,也不能访问在匿名方法外部使用的ref和out参数。但可以使用在匿名方法外部定义的其他变量。
如果需要用匿名方法多次编写同一个功能,就不要使用匿名方法。而编写一个指定的方法比较好,因为该方法只需编写一次,以后可通过名称引用它。
6.3.6 Lambda表达式
C# 3.0为匿名方法提供了一个新的语法:Lambda表达式。Lambda表达式可以用于委托类型。“Lambda表达式”是一个匿名函数,它可以包含表达式和语句,并且可用于创建委托或表达式目录树类型。
所有 Lambda 表达式都使用 Lambda 运算符 =>,该运算符读为“goes to”。该 Lambda 运算符的左边是输入参数(如果有),右边包含表达式或语句块。Lambda 表达式 x => x * x 读作“x goes to x * x”。可以将此表达式分配给委托类型,如下所示:
class Program { delegate int del(int i);
static void Main(string[] args) { del myDelegate = x => x * x; int j = myDelegate(5); //j的值为调用委托所得到的值 Console.WriteLine(j); //值为25 Console.ReadLine(); } } |
现在基本上明白了Lambda表达式的写法,接下来我们结合匿名方法来做一个实例:
class Program { delegate int del(int i);
static void Main(string[] args) { del myDelegate = x => x * x; int j = myDelegate(5); //j的值为调用委托所得到的值 Console.WriteLine(j); //值为25 //注意这里result实际上是del委托类的参数,所以一定是int类型 del d1 = result => { result += 10; //对传入的result参数做简单处理 return result; //返回结果 }; //注意大括号后面有一个分号作为结束
Console.WriteLine(d1(2)); //直接调用委托,结果显示12 Console.ReadLine(); } } |
老田:给你布置个任务,自定义一个多个参数的委托类型,结合Lambda表达式和匿名方法来做个实例。
小天:等等先问两个问题:
1. 为什么你上面第一个例题 del myDelegate = x => x * x; 这段代码中 => 符号后面的x * x 没有给大括号,但是第二个是给的大括号?
2. 多个参数在 => 符号前面怎么表示?
老田:第一个问题:如同if语句一样,如果下面只有一行代码,则可以省略大括号;例如下面这样也是对的
del myDelegate = x =>{return x * x}; |
第二个问题:多个参数任然写在=> 符号前面,不过需要用括号括起来,同时多个参数之间用逗号分隔,例如
Del d1 = (x, y, z) => z + y + z; |
小天:那就简单了。如下,我定义了一个名为XiaoTian的委托,两个参数,然后在匿名方法中对参数进行了简单的处理,最后返回这里我还做了点举一反三,如下:
class Program { //三个参数的委托 delegate string XiaoTian(string xr, int age, string sex);
static void Main(string[] args) { //多个参数就必须使用括号,参数之间用逗号分隔 XiaoTian xt = (xr, age, sex) => { StringBuilder jg = new StringBuilder(); jg.Append("小天此人今年"); jg.Append(age); jg.Append("岁,性别"); jg.Append(sex); jg.Append(",长相"); jg.Append(xr); return jg.ToString(); //直接返回结果 }; //调用委托 Console.WriteLine(xt("帅得一塌糊涂", 28, "男")); Console.ReadLine(); } } |
运行后效果如图6-6
图6-6
老田:不错。上面我们讨论的都是预定义参数的情况下,那么假设传入的参数类型是自定义的类型,恰恰编译器推断不出来怎么办呢?那么接下来就要说到的是在为参数指定类型,如下:
//自定义一个有两个属性的类 class Thc { public Thc(string n, int a) { Name = n; Age = a; } public string Name { get; set; } public int Age { get; set; } } //控制台应用程序的起始类 class Program { //使用自定义类型的参数 delegate string More(Thc t); static void Main(string[] args) { //对参数申明类型 More m =(Thc t )=> { return "姓名为" + t.Name + ",年龄" + t.Age; }; //调用委托,注意所给的参数是新实例一个对象 Console.WriteLine(m(new Thc("天轰穿", 30))); Console.ReadLine(); } } |
小天:上面说了这么多都是有参数的,如果遇上没有参数的咋办?
老田:没有参数更省事,直接给个空的括号就行,如下:
class Program { //无参数 delegate void Wucanshu();
static void Main(string[] args) { //对于无参数的情况就直接给一个空的括号就行了 Wucanshu wcs = () => { Console.WriteLine( "我就是无参数,你把我咋样嘛!"); }; //调用委托 wcs(); Console.ReadLine(); } } |