之前,我们学习总结了委托这个概念,也阐述了委托,匿名方法,lambda表达式三者之间的关系,那么今天再来继续学习委托更深层次的东西:泛型委托,什么是泛型,这个概念我也会在之后做出总结,这里不做很深层次的讨论,重点是讨论泛型和委托如何配合使用,其实泛型这个概念在这里也不会对我们对委托的理解有太大的影响,我们只要大概知道泛型就是一种动态的类型,它在使用时可以代表任意类型,下面我们再来回顾一下我们是如何定义普通委托的:
public delegate int 委托名(int a, int b);
这是委托的定义,它的定义有这几个特点,(1)可以用访问修饰符修饰。(2)delegate关键字。(3)有返回值和参数。
我们之前也说了,委托是一种类型,与之对应的方法必须和它具有相同的签名,即相同的参数个数,相同的参数类型和相同的返回值类型。我们回顾了普通委托之后再来看一下泛型委托的定义:
public delegate T 委托名<T>(T a, T b);
与之前不同的是,我们把int类型变成了万能的T类型,这样写的好处是什么呢?
可以想象,我们之前写了这样一个方法来处理加减乘除不同的计算方法:
static void Calculate(Expression ex, int a, int b) { Console.WriteLine(ex(a, b) + "\n"); }
仔细的你会发现我们封装的这个方法有很大的局限性,假如我们某天要计算Double,float小数类型或者其他类型的加减乘除时,我们是不是又不得不重载多个不同参数类型的Calculate方法,即:
static void Calculate(Expression ex, double a, double b) { Console.WriteLine(ex(a, b) + "\n"); } static void Calculate(Expression ex, float a, float b) { Console.WriteLine(ex(a, b) + "\n"); }
这样的话是不是代码又有重复了,那假设又几千种不同的类型要去计算呢?我们能不能只写一个方法就处理不同类型的加减乘除呢?这时,C#里有一个重要的工具:泛型的作用就体现出来了,我们可以把委托和方法定义成泛型的。代码如下:
public delegate T Expression<T>(T a, T b); class Program { static void Main(string[] args) { Expression<int> add = (a, b) => a + b; Calculate(add, 10, 25); Console.ReadKey(); } static void Calculate<T>(Expression<T> ex, T a, T b) { Console.WriteLine(ex(a, b) + "\n"); } }
我们只需在声明委托Expression<>时,为委托定义int类型就可以了,假如有一天,我要定义double类型,同理只需把Expression<int>换成Expression<double>即可,这样写是不是既节省了代码,又让Calculate方法的灵活性变高了。不管是lambda表达式还是泛型,微软可谓把DRY(Don't-repeat-yourself)原则发挥的淋漓尽致,其实微软早已为我们定义好了一套泛型委托供我们使用,以免我们在自己使用时还繁琐重复的去定义它,他们分别是Action,Func和Predicate
这是我在资料上摘取的这几个委托的区别:
(1). Action
Action是无返回值的泛型委托。
Action 表示无参,无返回值的委托
Action<int,string> 表示有传入参数int,string无返回值的委托
Action<int,string,bool> 表示有传入参数int,string,bool无返回值的委托
Action<int,int,int,int> 表示有传入4个int型参数,无返回值的委托
Action至少0个参数,至多16个参数,无返回值。
(2). Func
Func是有返回值的泛型委托
Func<int> 表示无参,返回值为int的委托
Func<object,string,int> 表示传入参数为object, string 返回值为int的委托
Func<object,string,int> 表示传入参数为object, string 返回值为int的委托
Func<T1,T2,,T3,int> 表示传入参数为T1,T2,,T3(泛型)返回值为int的委托
Func至少0个参数,至多16个参数,根据返回值泛型返回。必须有返回值,不可void
(3). Predicate
predicate 是返回bool型的泛型委托
predicate<int> 表示传入参数为int 返回bool的委托
Predicate有且只有一个参数,返回值固定为bool
一般的需求下,我们就使用微软定义的委托就足够了,这样减少了我们对委托的重复定义,可能有部分初学者见到Func<>,Action<>这样的代码肯定会很懵比,这只是你对新东西陌生罢了,多结合实例敲几遍,自然就会用了,它们其实就是微软封装定义好了的委托,没有什么特别的。我们上面的代码也可以这样写:
class Program { static void Main(string[] args) { Func<int,int,int> add = (a, b) => a + b; Calculate(add, 10, 25); Console.ReadKey(); } static void Calculate<T, Y, U>(Func<T, Y, U> ex, T a, Y b) { Console.WriteLine(ex(a, b) + "\n"); } }
这样写用Func就省去了定义委托这一步。
同样,其实在我们的webform,winform框架中,微软也给我们规范了一个委托的定义:
delegate void EvenHandler(object sender, EventArgs e);
都知道上面的object类是所有类型的基类,那EventArgs类呢?它其实就是所有包含事件数据类的基类,那什么是事件呢?,之后的学习总结中,我们来解谜C#另一个扑朔迷离的东西:事件