说来惭愧,做开发也有好几年了,但对委托依然一知半解,项目中也很少用到,这周在家仔细的看了下,写了点心得体会。
.net framework 基类库大量地使用了委托,那么什么是委托呢?
大家熟悉的常用数据类型(如int)的使用方法:先定义一个变量,然后给他赋值,如
Int I;//定义变量
I=100;//给变量赋值
类似的委托也可以看成是一种数据类型,可以用于定义变量,但它所定义的变量能接收的值只能是一个方法。
举个例子FirstDelegateExample:
//类MathOpt,定义了一下Add方法
Public cass MathOpt
{
Public int Add(int argument1,int argument2)
{
Return argument1+argument2;
}
}
//定义一个委托数据类型MathOptDelegate
Public delegate int MathOptDelegate(int value1,int value2)
上述委托跟前面定义的Add方法,两者都接收两个int参数,返回一个int数值,它们是不是”长得很像”,确实,这种相似不是偶然的
定义好委托数据类型后,就可以如此使用它。
MathOptDelegate oppDel; //定义此委托类型变量
MathOpt obj=new MathOpt();//实例化类
oppDel=obj.Add; //委托变量接收一个方法, 注意add后面没有括号
oppDel(1,2); //赋值之后委托变量可以当成普通方法一样使用,等同于Add(1,2)
此项目可以得出一个直观印像:委托可以看成是一个方法的”容器”,将某一具体的方法”装入”后,就可以把它当成方法一样使用。
那么是不是所有方法都可以赋值给oppDe变量呢?
仔细看MathOptDelegate定义语句,只能接收这样的方法,拥有两个int参数,并且返回值也是int。
所以只要满足上述要求,不管其名字如何,也不管它是静态还是实例,都可以赋值给oppDel,定义委托类型时对方法的要求被称为方法的”签名”
示例项目DynamicInvokeMethodForCS,用户输入两个操作数,再选择运算方法,程序即可算出结果。这个示例的关键之处在于它使用委托在运行时动态的调用不同方法
示例程序UseDelegateForm:点击从窗体按钮,主窗体会记录下对按钮的单击次数,这个示例关键在于主从窗间信息的传送用到了ShowInfoDelegate
从上面介绍的内容可知,委托变量可以引用某一方法,调用它就相当于调用这个方法
仔细想想,是不是会有以下疑惑:
如果委托仅仅是方法调用的另一种方式,那何必多此一举引入委托这一特性?直接调用方法不是更简洁明了?
其实委托不仅可以引用一个方法,还可以组合多个方法并批量执行它们,看以下示例
delegate void MyDelegate(string s);
class Program
{
static void Main(string[] args)
{
MyDelegate a,b,c,d;
a=MyClass.Hello;
Console.WriteLine("调用托变量a");
a("a");
b = MyClass.GoodBye;
Console.WriteLine("调用托变量b");
b("b");
c = a + b;
Console.WriteLine("调用托变量c");
c("a+b");
d = c - a;
Console.WriteLine("调用托变量d");
d("c-a");
Console.ReadKey();
}
}
class MyClass
{
public static void Hello(string s)
{
Console.WriteLine("您好,{0}", s);
}
public static void GoodBye(string s)
{
Console.WriteLine("再见,{0}", s);
}
}
执行结果:
上述代码委托变量c组合了委托变量a+b,因而拥有了两个方法,当执行c(“a+b”)时将导致MyClass类的两个静态方法都被执行
像这样的委托变量称为”多路委托变量”
可以用加法运算符来给合单个委托变量为多路委托变量,同样的用减法运算符从一个多路委托变量中除移某个委托变量
委托之所以有这种特性,是因为它内部可以包含多个方法引用,这些方法引用被称为”委托调用列表”
首先通过一个示例
class Program
{
static void Main(string[] args)
{
MyGenericDelegate<int> del = MyFunc;
Console.WriteLine(del(10));
Console.ReadKey();
}
static int MyFunc(int value)
{
return value;
}
}
public delegate T MyGenericDelegate<T>(T obj);
泛型委托跟普通委托类似,不同之处在于使用泛型委托时需要指定泛型参数,除此之外没任何神秘之处。
为了方便开发,.NET基类库针对在实际开发中最常用的情形提供了几个预定义好的委托
Func系列委托用于引用一个有返回值的方法
Func委托声明的最后一个泛型参数是委托所接收方法的返回值类型,前面的泛型类型参数(如果有的话)就是委托所接收方法的形参类型
示例如下:
class Program
{
static void Main(string[] args)
{
Func<int, int, long> func = Add;
Console.WriteLine(func(10, 20));
Func<string, string, string> func1 = LinkStr;
Console.WriteLine(func1("你好:", "鱼"));
Console.ReadKey();
}
static long Add(int x, int y)
{
return x + y;
}
static string LinkStr(string a, string b)
{
return a + b;
}
}
还有另外常用的预定义委托,大家自行查阅资料
Action系列委托:接收返回为void的方法
Predicate委托:引用一个返回bool值的方法
总结一下上面的使用委托步骤
1.定义委托类型
2.定义一个或多个符合委托类型要求的方法
3.定义委托类型的变量
4.将2中定义的方法引用”挂接”到3步中定义的变量,构建一个委托调用列表
5.能过委托变量间接调用方法
上述步骤是不是很烦锁,基于上述简化开发的考虑,C#引入了匿名方法和Lambda表达式
示例代码如下注意加粗字:
class Program
{
static void Main(string[] args)
{
AddDelegate del = delegate(int i, int j)
{
return i + j;
};
Console.WriteLine(del(10, 20));
Console.ReadKey();
}
}
public delegate int AddDelegate(int i,int j);
匿名方法其实是将方法定义与委托变量赋值两个步骤合在一起,从而省掉了单独定义一个方法的麻烦
上面的代码如果用Lambda表达式,可以如此简化
class Program
{
static void Main(string[] args)
{
SomeDelegateType del2 = argument => { return argument.ToString(); };
Console.WriteLine(del2(10));
Console.ReadKey();
}
}
public delegate string SomeDelegateType(int argument);
Lambda表达式其实就是匿名方法的进一步简化,可以用于定义一个匿名函数,并将其传送给一个委托变量
Lambda有两种基本格式
1.(input parameter)=>表达式
2.(input parameter)=>{语句1;语句2;}
1)只有一个参数时,括号是可选的,Lambda表达式只有一条return语句时,return关键字也可省
Func<int,bool> del1=(x)=>{return x>0};
可以简化为
Func<int,bool> del1=x=>{x>0};
2)两个或更多输入参数由逗号分隔
Func<int,int,bool> del2=(int x,int y)=>{return x==y};
3)没有输入参数时,直接使用一个空的括号()
Action del4=()=>{Console.WriteLine("No argument");};