.net委托小结
看了CLR Via C#的委托一章,有些新收获,做一下总结。
一.认识委托
委托也是.net的一种类型,它可以包装与它签名相同的函数,对函数进行注册或删除,然后可以向调用函数本身一样执行,多播委托则按顺序调用函数。
委托的定义如下: delegate 返回值类型 委托名称(参数)。
委托可以定义在类的内部或定义为全局,返回值类型不能为值类型(如int,double等,具体原因不太清楚),否则编译器会报错。
查看IL可以发现,委托被定义为一个类,继承自MultiCastDelegate。先看它的构造函数,有2个参数object和method,(1)如果被包装函数是static的,object为null,为instance的,则指向该实例;(2)method为包装的函数。然后看invoke方法,可以看出,它具有和被包装函数同样的签名,这个函数是最终被委托执行的函数。
二.多播委托
委托默认都是多播的,把函数注册到委托对象的方法是combine,注销的方法是remove,都是静态的。
如 delegate void FeedBack(string str);
Void function1(string str){//};
Void function2(string str){//};
FeedBack fb = null;
fb = FeedBack.Combine(new FeedBack(function1)) as FeedBack;
fb = FeedBack.Combine(new FeedBack(function2)) as FeedBack;
//remove function2
fb = FeedBack.remove(fb,new FeedBack(function2)) as FeedBack;
也可以用重载符号+,-来完成注册和注销。
多播委托调用时的执行顺序是按照注册的顺序执行的,委托对象内有个数组
来维护。
多播委托有些问题,例如:函数如果有返回值,将不能得到;还有,如果其中间某个函数执行发生了异常,其后面的函数将不再执行。
如何解决多播委托的这一问题呢,也就是如何能显示的控制对函数的调用,有一个方法,MultiCastDelegate有个方法叫GetInvactionList(),它返回一个
Delegate数组,这个数组对应包装的函数,对其进行循环迭代,可以显示的控制函数的调用,并且处理中间发生的异常。
例子代码如下:
using System;
using System.Collections.Generic;
public delegate string GetStatus();
/// <summary>
/// MultiCastDelegate类的GetInvocationList()函数可得到Delegate数组,从而可以显示的控制对多播委托的调用。
/// </summary>
class Program
{
static void Main(string[] args)
{
GetStatus getStatus = null;
getStatus += new GetStatus(new Light().SwitchPosition);
getStatus += new GetStatus(new Fan().Speed);
getStatus += new GetStatus(new Speaker().Volume);
Console.WriteLine(GetComponentsStatusReport(getStatus));
}
public static string GetComponentsStatusReport(GetStatus getStatus)
{
if (getStatus == null)
{
return null;
}
StringBuilder report = new StringBuilder();
Delegate[] arrayOfDelegates = getStatus.GetInvocationList();
foreach (GetStatus g in arrayOfDelegates)
{
try
{
report.AppendFormat("{0}{1}{1}", g(), Environment.NewLine);
}
catch (InvalidOperationException e)
{
object target = getStatus.Target;
report.AppendFormat("Failed to get status from {1}{2}{0} Error: {3}{0}{0}", Environment.NewLine,
((target == null) ? "" : target.GetType() + "."), getStatus.Method.Name, e.Message);
}
}
return report.ToString();
}
}
internal sealed class Light
{
public string SwitchPosition()
{
return "The light is off";
}
}
internal sealed class Fan
{
public string Speed()
{
throw new InvalidOperationException("The fan broke due to overheating");
}
}
internal sealed class Speaker
{
public string Volume()
{
return "The volume is loud";
}
}
执行结果输出:
The light is off
Failed to get status from MultiCastDelegateDemo.Speaker.Volume
Error: The fan broke due to overheating
The volume is loud
三.Dotnet对委托的简化操作
1. 匿名函数,如:
this.button1.Click += delegate(object send, EventArgs ev) { MessageBox.Show("Anonymous Delegate with parameters! "); };
2. 如果函数不使用参数,可以省略,如:
this.button1.Click += delegate { MessageBox.Show("Anonymous Delegate without parameters!"); };
四.反射与委托
对于在编译时,不知道委托类型和函数类型时,可以使用反射来做。Delegate类有CreateDelegate()的多种重载形式,用反射来创建Delegate对象,如
CreateDelegate(Type type,MethodInfo method),type为委托定义类型,method为包装函数,得到Delegate对象后,有个DynamicInvoke(参数)函数,可以动态执行调用。
例子如下:
internal delegate object ToInt32s(Int32 n1,Int32 n2);
internal delegate object OneString(string s);
class Program
{
static void Main(string[] args)
{
if (args.Length < 2)
{
string fileName = Path.GetFileNameWithoutExtension(Assembly.GetEntryAssembly().Location);
string usage = @"Usage:" +
"{0}{1} delType methodName [Arg1] [Arg2]" +
"{0} where delType must be TwoInt32s or OneString" +
"{0} if delType is ToInt32s,methodName must be Add or Sutract" +
"{0} if delType is OneString,methodName must be NumChars or Reverse" +
"{0} " +
"{0}Examples: " +
"{0} {1} ToInt32s Add 123,321" +
"{0} {1} ToInt32s Subtract 321,123" +
"{0} {1} OneString NumChars /"hello /"there " +
"{0} {1} OneString Reverse /"hello /"there ";
Console.WriteLine(usage, Environment.NewLine, fileName);
return;
}
Type delType = Type.GetType(args[0]);
if (delType == null)
{
Console.WriteLine("Invalid type argument:" + args[0]);
return;
}
Delegate d;
try
{
MethodInfo mi = typeof(Program).GetMethod(args[1], BindingFlags.NonPublic | BindingFlags.Static);
d = Delegate.CreateDelegate(delType, mi);
}
catch (ArgumentException e)
{
Console.WriteLine("Invalid methodName argument:" + args[1]);
return;
}
object[] callbackArgs = new object[args.Length - 2];
if (d.GetType() == typeof(ToInt32s))
{
try
{
for (int i = 2; i < args.Length; i++)
{
callbackArgs[i - 2] = int.Parse(args[i]);
}
}
catch (FormatException e)
{
Console.WriteLine("Parameters must be integer");
return;
}
}
if (d.GetType() == typeof(OneString))
{
Array.Copy(args, 2, callbackArgs, 0, callbackArgs.Length);
}
try
{
object result = d.DynamicInvoke(callbackArgs);
Console.WriteLine("Result="+result);
}
catch(TargetParameterCountException)
{
Console.WriteLine("Incorrect number of parameters specified.");
}
}
private static object Add(Int32 n1, Int32 n2)
{
return n1 + n2;
}
private static object Subtract(Int32 n1, Int32 n2)
{
return n1 - n2;
}
private static object NumChars(string s)
{
return s.Length;
}
private static object Reverse(string s)
{
Char[] chars = s.ToCharArray();
Array.Reverse(chars);
return chars.ToString();
}
配置参数如下:ToInt32s Add 123 321,得到的结果为:result=444