接上篇(C#委托详解(2):实现方式大全),本篇继续介绍委托的实现方式。
4、Action<T>和Func<T>委托
使用委托时,除了为每个参数和返回类型定义一个新委托类型之外,还可以使用.NET Framework提供的泛型委托Action<T>和Func<T>,它们提供了从无参一直到最多16个参数的重载,如果方法需要获取16个以上的参数,就必须定义自己的委托类型,但这种情况应该是及其罕见的。其中Action<T>类可以调用没有返回值的方法,Func<T>允许调用带返回类型的方法。
所以对于上一代码例子中的委托可以使用Func<double T, out Tresult>这样实现:
Func <double, double> operations = MathOperations.MultiplyByTwo;
等价于:
delegate double Operations(double x);
Operations operations = MathOperations.MultiplyByTwo;
即省略了对于委托类的定义。
下面这个例子使用泛型委托实现了一个普通的冒泡排序算法,来看看跟普通的冒泡排序有什么不一样的:
class BubbleSorter { static public void Sort<T>(IList<T> sortArray, Func<T, T, bool> comparison) { bool swapped = true; while (swapped) { swapped = false; for (int i = 0; i < sortArray.Count - 1; i++) { if (comparison(sortArray[i+1], sortArray[i])) { T temp = sortArray[i]; sortArray[i] = sortArray[i + 1]; sortArray[i + 1] = temp; swapped = true; } } } } }
可以看到,代码实现没有什么不同,不同的是方法签名,第一个参数类型是IList<T>,它可以支持传入各种集合数据,第二个传入参数为一个委托对象,代表的是要排序的对象T的一个方法,这个方法会对两个对象T的大小进行比较,返回一个boolean类型的值,具体使用方法可以查看下面这段代码。
class Student //需要排序的对象类型 { public Student(string name, int salary) { this.Name = name; this.Age = salary; } public string Name { get; private set; } public int Age { get; private set; } public override string ToString() { return string.Format("{0}, {1:C}", Name, Age); } public static bool CompareAge(Student e1, Student e2) { return e1.Age < e2.Age; } } class Program { static void Main() { Student[] students = { new Student("zhangsan", 20), new Student("lisi", 12), new Student("wangwu", 25), new Student("zhaoliu", 10), }; BubbleSorter.Sort(students, Student.CompareAge); //传入对象数组,和一个方法地址 foreach (var student in students) { Console.WriteLine(student); } } }
上述代码中展示了委托的精髓,即将方法作为参数传递给其他方法,以增加代码的灵活性。(不清楚的童鞋一定仔细体会这种用法。)
5、匿名方法
使用匿名方法定义委托与前面的定义没有什么区别,只是在实例化委托时,有一点区别。
class Program { static void Main() { double width = 12; Func<double, double> square = delegate(double length) //这里的square是委托类型的实例 { length *= width; return length; }; Console.WriteLine(square(10)); } }
匿名方法的优点是减少了要编写的代码,不必定义仅由委托使用的方法。但如果需要用匿名方法多系编写同一个功能时就不建议使用匿名方法了。
6、Lambda表达式
C#3.0开始支持Lambda表达式,可以使用其代替匿名方法。使用方法可以参考下面的例子:
//1、不获取任何参数 Func<string> f = () => "lining"; //2、一个参数(可以显示指定类型也可以使用类型推断) Func<int, string> f1 = (int n) => n.ToString(); Func<int, string> f2 = (n) => n.ToString(); Func<int, string> f3 = n => n.ToString(); //3、一个或多个参数 Func<int, int, string> f4 = (int n1, int n2) => (n1 + n2).ToString(); Func<int, int, string> f5 = (n1,n2) => (n1+n2).ToString();
Lambda省去了书写方法,命名方法及传递方法的过程,可以减少代码量,提高编程效率。但其并没有对代码的执行效率带来优势。
另外建议不要滥用lambda表达式,其对于调试和单步执行会带来一定的挑战。对于三行以上的代码推荐自己手动写一个方法,这样无论对于代码的可读性还是可服用性都是有益的。
本篇结束,对于委托的实现方式就先介绍这些,下一篇将介绍委托的Invoke,BeginInvoke和EndInvoke方法。