匿名方法的实现原理就是:编译器将在匿名方法所在的类,为每个匿名方法都创建了一个方法。编译器创建的这些方法只在IL代码中有效,在C#代码中是无效的,所以C#代码不能直接使用这些方法。
其实,匿名方法更常用的地方是把匿名方法当作一个参数传递给另一个方法。大家肯定都知道List有一个FindAll的方法来查找符合条件的item,这里FindAll的参数就是一个过滤条件的委托。
List<int> numList = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; foreach(int num in numList.FindAll(delegate(int n){return n>6;})) { Console.WriteLine(num); }
说到了这里,我们就看看系统帮我们定义的委托,在C#中,Action、Func和Predicate是系统定义的委托,我们可以直接使用。上面的FindAll的参数就是一个Predicate<T>的泛型委托。
Action委托Action<T>是无返回值的泛型委托,Action委托可以支持至少0个参数,至多16个参数。
例如:
Action 表示无参,无返回值的委托
Action<int,string> 表示有传入参数int,string无返回值的委托
Action<int,string,bool> 表示有传入参数int,string,bool无返回值的委托
对于前面的字符串反转的例子,我们可以使用Action委托进一步简化,这样我们连"ReverseStringHandler"这个委托也省略了:
static void Main(string[] args) { Action<string> reverseString = delegate(string str) { char[] charArray = str.ToCharArray(); Array.Reverse(charArray); Console.WriteLine(charArray); }; reverseString("Hello World"); Console.Read(); }
Func委托前面看到Action委托是没有返回值的,为了解决我们有时可能需要返回值的问题,系统中又出现了Func委托。
Func<TResult>是有返回值的泛型委托,其中TResult就代表返回值的类型()。Func委托可以支持至少0个参数,至多16个参数(Func<T1,T2,…,T16,TResult>)。
例如:
Func<int> 表示无参,返回值为int的委托
Func<object,string,int> 表示传入参数为object, string 返回值为int的委托
Func<object,string,int> 表示传入参数为object, string 返回值为int的委托
看个简单的例子:
Func<int, int, int> addOperation = delegate(int numA, int numB) { return numA + numB; }; Console.WriteLine(addOperation(3,4));
Predicate委托predicate 是返回bool型的泛型委托,常常结合集合类的查询使用;Predicate有且只有一个参数,返回值固定为bool。
Predicate原型:public delegate bool Predicate<T> (T obj)
T: 要比较的对象的类型。
obj: 要按照由此委托表示的方法中定义的条件进行比较的对象。
返回值:如果 obj 符合由此委托表示的方法中定义的条件,则为 true;否则为 false。
在前面结合查询的例子中,我们直接把匿名方法"delegate(int n){return n>6;}"传给了FindAll方法。
其实也可以写成,
Predicate<int> checkNum = delegate(int n) { return n > 6; }; foreach (int num in numList.FindAll(checkNum)) {……}
忽略委托参数在有些情况下,我们并不需要委托参数,那么匿名方法可以进一步省略参数列表,只需要使用一个delegate关键字,加上作为方法的操作使用的代码块。
看一个简单的代码段:
Action printer = delegate { Console.WriteLine("Hello world"); }; printer();
注意,这个"参数通配"(paremeter wildcarding)的特性并不能适用所有的情况,如果匿名方法能够转换成多个委托类型,那么我们就需要给编译器提供更多的信息。
举个例子,线程的构造函数设计两个委托类型,一个有参数,一个无参数。
public delegate void ParameterizedThreadStart(object obj)
public delegate void ThreadStart()
所以,当我们通过下面的语句创建线程的时候,前两条语句没有问题,但是第三条语句会有一个错误。
因为第三条语句中的匿名方法可以转换成多个委托类型,编译器就不知道怎么处理了,所以,我们需要显示给出参数列表。