C# 无返回类型的内置委托—Action
1.1 初识Action
MSDN给出的定义:封装一个方法,该方法不具有参数并且不返回值。
可以使用此委托以参数形式传递方法,而不用显式声明自定义的委托。封装的方法必须与此委托定义的方法签名相对应。也就是说,封装的方法不得具有参数,并且不得返回值。(在 C# 中,该方法必须返回 void)通常,这种方法用于执行某个操作。
现在,我们来看看如何使用Action委托:
(1)先看看之前我们是怎么来使用无返回值委托的例子:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public delegate void ShowValue(); public class Name { private string instanceName; public Name(string name) { this.instanceName = name; } public void DisplayToConsole() { Console.WriteLine(this.instanceName); } public void DisplayToWindow() { MessageBox.Show(this.instanceName); } } public class testTestDelegate { public static void Main() { Name testName = new Name("Koani"); ShowValue showMethod = testName.DisplayToWindow; showMethod(); } }
可以清楚地看出,我们之前要先显式声明了一个名为 ShowValue 的委托,并将对 Name.DisplayToWindow 实例方法的引用分配给其委托实例。
(2)再看看有了Action委托之后我们怎么来达到上面的效果的例子:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class Name { private string instanceName; public Name(string name) { this.instanceName = name; } public void DisplayToConsole() { Console.WriteLine(this.instanceName); } public void DisplayToWindow() { MessageBox.Show(this.instanceName); } } public class testTestDelegate { public static void Main() { Name testName = new Name("Koani"); Action showMethod = testName.DisplayToWindow; showMethod(); } }
可以清楚地看出,现在使用 Action 委托时,不必显式定义一个封装无参数过程的委托。
1.2 深入Action
在实际开发中,我们经常将一个委托实例作为一个方法的参数进行传递,于是我们来看一下这个典型的场景,再通过Reflector反编译工具查看编译器到底帮我们做了什么好玩的事儿!
(1)首先来看一下在List集合类型的ForEach方法的定义:
// // 摘要: // 对 System.Collections.Generic.List<T> 的每个元素执行指定操作。 // // 参数: // action: // 要对 System.Collections.Generic.List<T> 的每个元素执行的 System.Action<T> 委托。 // // 异常: // System.ArgumentNullException: // action 为 null。 public void ForEach(Action<T> action);
可以看出,ForEach方法的参数是一个Action委托实例,也就是说是一个无返回值的委托实例。
(2)定义一个实体类,并通过Action委托使用ForEach方法:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class Person { public int ID { get; set; } public string Name { get; set; } public int Age { get; set; } } static void ActionDelegateDemo() { List<Person> personList = GetPersonList(); personList.ForEach(new Action<Person>(delegate(Person p) { Console.WriteLine(p.ID + "-" + p.Name + "-" + p.Age); })); }
可以看出,我们为ForEach方法传递了一个Action委托的实例,本质上是一个无返回值的方法指针,遍历输出了每个Person对象的信息。
(3)也许有些童鞋看到上面的还是有点不解,只要你了解过委托,那么我们可以通过Reflector反编译工具去看看编译器到底做了啥事,Action委托的本质就会一如了然:(这里我们可以先看看没有Action的做法,是不是需要首先显式声明了一个无返回值的委托,然后是不是还要顶一个命名的无返回值的方法?)
①将编译好的程序集拖动到Reflector中,可以看到以下的情形:
②现在分别看看编译器为我们自动生成的无返回值的委托定义和方法定义:
可以看出,不管是自动生成的委托还是方法,都是不带返回值的。
③有了上面的分析,我们再来看看执行的语句是怎么被编译的:
可以看出,在编译后的代码里边连new Action<Person>()都省掉了,我们也可以知道,在代码中可以更加简化。但是,首先,我们得了解到底编译器是怎么识别Action委托的。于是,按照前两篇的思路,在反编译后的C#代码看不出什么端倪的时候,切换到IL代码一探究竟:
由IL代码可以看出,还是原来的方法,还是原来的味道。委托还是那个委托,执行委托还是执行那个方法。这里,我们再来看看List类型的ForEach方法是怎么使用Action委托的:
现在,我们可以知道,原来所不解的东西现在终于释怀了:在ForEach会通过一个循环遍历依次调用委托所持有的方法,这个方法是一个符合Action委托定义的无返回值方法。至于,为什么我们可以省略new Action<T>(),则是编译器为我们提供的一个便利。例如,我们在使用List<Person>对象的ForEach方法时,我们可以这样写:
personList.ForEach(delegate(Person p) { Console.WriteLine(p.ID + "-" + p.Name + "-" + p.Age); });
首先,由于我们是使用的personList这个对象(List<Person>类型),所以编译器自动识别了泛型委托的T(即指定类型)为Person。其次,编译器自动将无返回值的匿名方法转换为了new Action<Person>对象。当然,如果是有返回值的匿名方法则会转换为指定类型的new Func<T>()对象,这里因为ForEach只接受无参数的委托实例或方法,所以如果传入了有返回值的匿名方法则会报错。
1.3 你究竟有几个Action可用?
从图中可以看出,.NET Framework为我们提供了多达16个参数的Action委托定义,对于常见的开发场景已经完全够用了。
出处:http://edisonchou.cnblogs.com