与委托相关的知识点有很多,本节仅仅就委托的协变和逆变、匿名方法、方法组转换与Lambda表达式做一些介绍。
8.5.1 委托的协变和逆变
在面向对象设计程序中,允许把派生类的对象赋值给基类的对象,如果从继承关系来看,允许从底层对象向高层对象的转换,并且这种转换是自动的。根据这一规律,对于委托来说可以得出两个有用的知识点。这个就是协变与逆变。
协变:从一个方法或者函数的返回类型来看,返回子类的函数或者方法都可以绑定到具有基类返回类型的委托上面。例如:如何将委托与具有返回类型的方法一起使用,这些返回类型派生自委托签名中的返回类型。由 SecondHandler 返回的数据类型是 Dogs 类型,它是由委托中定义的 Mammals 类型派生的。
class Mammals
{
}
class Dogs : Mammals
{
}
class Program
{
//定义一个委托,该委托返回类型是Mammals
public delegate Mammals HandlerMethod();
public static Mammals FirstHandler()
{
return null;
}
public static Dogs SecondHandler()
{
return null;
}
static void Main()
{
HandlerMethod handler1 = FirstHandler;
//协变允许委托这样绑定方法
HandlerMethod handler2 = SecondHandler;
}
}
逆变:如果从函数或者方法的参数来考虑,那么具有基类参数的函数或者方法都可以绑定到具有子类参数签名的委托上面。比如,现在可以创建一个接收 EventArgs 输入参数的事件处理程序MultiHandler,然后,可以将该处理程序与发送 MouseEventArgs 类型(作为参数)的 Button.MouseClick 事件一起使用,也可以将该处理程序与发送 KeyEventArgs 参数的 TextBox.KeyDown 事件一起使用。因为MouseEventArgs与KeyEventArgs都是从EventArgs派生出来的子类。
System.DateTime lastActivity;
public Form1()
{
InitializeComponent();
lastActivity = new System.DateTime();
this.textBox1.KeyDown += this.MultiHandler; this.button1.MouseClick += this.MultiHandler;
}
private void MultiHandler(object sender, System.EventArgs e)
{
lastActivity = System.DateTime.Now;
}
8.5.2匿名方法
在程序中,经常有这样一些需求:
1)需要一个临时方法,这个方法只会使用一次,或者使用的很少。
2)这个方法的方法体很短,以至于比方法声明都短。
在以前,即使有上面的情况,我们还是不得不在类里面写出这两个方法。增加了类代码中小型方法的混乱。而且要想使委托工作,委托也要求方法必须已经存在。可是通过匿名方法,上面的情况都可以避免。
匿名方法可以将代码块传递为委托参数。用匿名方法定义委托的语法与前面的定义并没有什么区别。例如
delegate int Del(int x);
Del d = delegate(int k) { return k*k };
可以看到使用格式:
delegate(参数类型 参数){ 对参数加工的语句块;}
能够让系统生成一个方法或者函数,只是这个委托没有具体的名字,这个方法或者函数可以直接绑定给具有相同方法签名的委托。可能你已经注意到,delegate(int k) { return k*k };中并没有显式给出返回值的类型,这个工作系统会自动的完成,可以根据代码的返回值自动推断出类型。
匿名方法最明显的好处就是可以降低重新写一个方法的工作量,特别是这些方法的实现语句很小,或者被使用的机会很少。另外一个好处就是可以访问调用者的变量,降低传参数的复杂度,下面就通过一些使用例子来具体看看。
假设现在有一个城市名称的列表list,现在需要从list中找到符合一定条件的城市的名称,并打印出来。比如找到list中所有以字符x与y开头的城市名称。这些要求可能很多。按照传统的写法,每一个要求都需要写成一个方法。下面就是例子AnonymityMethod的代码:
namespace AnonymityMethod
{
class Program
{
static void Main(string[] args)
{
List<string> list = new List<string>();
list.Add("xian");
list.Add("shanghai");
list.Add("beijing");
list.Add("nanjing");
list.Add("xuchang");
list.Add("nanchang");
list.Add("yanan");
list.Add("xiangfan");
list.Add("yuncheng");
//查找以x开始的城市名称,结果放入ResultList
List<string> ResultList = list.FindAll(FindCitysByFirstX);
foreach (string item in ResultList)
{
Console.WriteLine(item);
}
//查找以y开始的城市名称,结果放入ResultList
ResultList = list.FindAll(FindCitysByFirstY);
foreach (string item in ResultList)
{
Console.WriteLine(item);
}
Console.ReadKey();
}
//找到以x开始的城市名
public static bool FindCitysByFirstX(string city)
{
return city[0] == 'x';
}
//找到以y开始的城市名
public static bool FindCitysByFirstY(string city)
{
return city[0] == 'y';
}
}
}
例子中有两种规则,那么就要定义两个方法,如果使用匿名方法的话,代码就会很简单,把例子AnonymityMethod改写成例子AnonymityMethod2。
namespace AnonymityMethod2
{
class Program
{
static void Main(string[] args)
{
List<string> list = new List<string>();
list.Add("xian");
list.Add("shanghai");
list.Add("beijing");
list.Add("nanjing");
list.Add("xuchang");
list.Add("nanchang");
list.Add("yanan");
list.Add("xiangfan");
list.Add("yuncheng");
//查找以x开始的城市名称,结果放入ResultList
List<string> ResultList = list.FindAll(delegate(string city)
{
return city[0] == 'x';
}
);
foreach (string item in ResultList)
{
Console.WriteLine(item);
}
//查找以y开始的城市名称,结果放入ResultList
ResultList = list.FindAll(delegate(string city)
{
return city[0] == 'y';
}
);
foreach (string item in ResultList)
{
Console.WriteLine(item);
}
Console.ReadKey();