之前的博客讲到委托,委托本质上是将方法作为方法的参数传给方法。实际开发中,实现某个功能的的代码通常会封装成一个类,本例中字符串处理封装成MyStringProc类,
代码如下:
1 namespace DelegateTest 2 { 3 public delegate string MyStringProcDelegate(string str); 4 class MyStringProc 5 { 6 public string ProcString(string str,MyStringProcDelegate strProcDelegate) 7 { 8 return strProcDelegate(str); 9 } 10 } 11 }
在调用的时候实例化这个类,再调用对应的方法。如下:
1 namespace DelegateTest 2 { 3 class Program 4 { 5 static void Main(string[] args) 6 { 7 MyStringProc msp = new MyStringProc(); 8 string str1=msp.ProcString("I am good.", StringToLower); 9 string str2 = msp.ProcString("I am good too", StringToUpper); 10 Console.WriteLine("{0},{1}",str1,str2); 11 Console.ReadKey(); 12 } 13 static string StringToLower(string str) 14 { 15 return str.ToLower(); 16 } 17 static string StringToUpper(string str) 18 { 19 return str.ToUpper(); 20 } 21 } 22 }
输出如下:
现在如果要实现某个功能,这个功能包含多种类型但操作参数都相同,只是内部逻辑不同,而且要依次调用其中几个,这时我们可以只定义一个委托变量,将这些操作的方法依次绑定到这个委托变量即可。
下面是处理一个字符串:字符串前后加'[]',前后后加'{}',代码如下:
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 MyStringProc msp = new MyStringProc(); 6 MyStringProcDelegate strProcDelegate; 7 strProcDelegate = StringProc1; 8 strProcDelegate += StringProc2; 9 msp.ProcString("I am a good boy",strProcDelegate); 10 Console.ReadKey(); 11 } 12 13 static string StringProc1(string str) 14 { 15 str= "["+str+"]"; 16 Console.WriteLine(str); 17 return str; 18 } 19 static string StringProc2(string str) 20 { 21 str= "{" + str + "}"; 22 Console.WriteLine(str); 23 return str; 24 } 25 }
输出如下:
以上并没有达到完全封装,本例用到的MyStringProcDelegate类型的委托变量是可以封装在MyStringProc类中,客户端直接调用该类的方法即可,无需再声明委托变量。如下:
1 public delegate string MyStringProcDelegate(string str); 2 class MyStringProc 3 { 4 public MyStringProcDelegate strProcDelegate; 5 public string ProcString(string str) 6 { 7 if (strProcDelegate!=null) 8 str=strProcDelegate(str); 9 return str; 10 } 11 }
调用代码如下:
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 MyStringProc msp = new MyStringProc(); 6 msp.strProcDelegate = StringProc1; 7 msp.strProcDelegate += StringProc2; 8 msp.ProcString("I am a good boy"); 9 Console.ReadKey(); 10 } 11 static string StringProc1(string str) 12 { 13 str= "["+str+"]"; 14 Console.WriteLine(str); 15 return str; 16 } 17 static string StringProc2(string str) 18 { 19 str= "{" + str + "}"; 20 Console.WriteLine(str); 21 return str; 22 } 23 }
输出如下:
事件
上面的改进无需再客户端声明委托变量,直接调用功能类的方法即可,但是委托变量声明成了public类型,意思就是说客户端可以随意操作该委托变量,破环了面向对象中的封装性。
假如我们将委托变量进行封装,类似于对字段的封装成属性,在c#中event就是对委托类型变量的一种封装,加上event关键字实际上是将普通的委托封装成具有Add和Remove方法
的一种特殊的委托,后面我用reflector反编译工具查看。
代码修改如下:
1 public delegate string MyStringProcDelegate(string str); 2 class MyStringProc 3 { 4 public event MyStringProcDelegate strProcDelegate; 5 public string ProcString(string str) 6 { 7 if (strProcDelegate != null) 8 str = strProcDelegate(str); 9 return str; 10 } 11 }
调用方法如下:
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 MyStringProc msp = new MyStringProc(); 6 msp.strProcDelegate += StringProc1; 7 msp.strProcDelegate += StringProc2; 8 msp.ProcString("I am a good boy"); 9 Console.ReadKey(); 10 } 11 static string StringProc1(string str) 12 { 13 str= "["+str+"]"; 14 Console.WriteLine(str); 15 return str; 16 } 17 static string StringProc2(string str) 18 { 19 str= "{" + str + "}"; 20 Console.WriteLine(str); 21 return str; 22 } 23 }
输出如下:
这样做的好处是,限定了委托的使用,可防止已注册该事件的方法被非法调用。注:事件只能用+=或-=。
下面是对定义的strProcDelegate 变量反编译结果,从中可看出我们在类中定义的该委托变量最终会编译成add、remove两个方法,add为对委托注册方法,remove为对委托取消注册方法。
而且虽然strProcDelegate 变量声明称public,但最终会编译成private,如下:
MyStringProcDelegate委托类型最终编译如下: