09.C#嘱托转换和匿名方法(五章5.1-5.4)
今天将书中看的,自己想的写出来,供大家参考,不足之处请指正。进入正题。
在C#1中开发web form常常会遇到使用事件,为每个事件创建一个事件处理方法,在将方法赋予给事件中,会使用new EventHandler(),不同的事件有各种不同的EventHandler的派生类的实例,因为我这里使用的时Console App,原理是一样的,且看
//定义一个委托 delegate void Printer(); static void Main(string[] args) { Printer p = new Printer(Print1); p += new Printer(Print2); p += new Printer(Print3); p.Invoke(); Console.ReadKey(); } static void Print1() { Console.WriteLine("print1"); } static void Print2() { Console.WriteLine("print2"); } static void Print3() { Console.WriteLine("print3"); }
可以看到每次给p委托一个方法时,都要new Printer(),参数为要传入的方法,可以想象在不同的委托时,要用代码显式地创建各种委托,过于繁琐。而在C#2中支持从方法组到一个兼容委托类型的隐式转换,自己想当然的理解为C++中的复制构造函数,如Printer p = Print1其实就是调用了new Printer(Print1),代码如下。
1 Printer p = Print1; 2 p += Print2; 3 p += Print3; 4 p.Invoke();
Printer p = Print1调用了Printer类的构造函数,而+=操作应该是Printer类型重载了+操作,用于两个Printer类相加,而方法组实现隐式转换,Print2和Print3隐式转换为Printer类的实例,C#2这样的操作减少了代码的输入,且更为直观,如"我只是把一个方法给到了一个对象,让它帮我执行,我才不管你要让传什么样的类型"。
- 泛型委托的协变性
- 委托返回的逆变性
对于第一点,可以理解为要给委托一个方法,用这个方法创建的委托可以是另一个委托的派生类,如所有事件处理的基类是EventHandler,当一个事件需要传入一个方法,则可以使用这个方法来创建一个从EventHandler中派生的类的实例,不用事件我不会用代码表示,请大牛告知。
对于第二点,委托定义返回类型中,如果返回类型有派生类,如在实现的方法中返回其派生类,则可以使用该方法创建委托。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
匿名方法,匿名方法使用delegate关键字,允许你指定一个内联委托实例的操作,匿名方法返回一个委托的实例。
C#2中许多内建的泛型委托,如Action(T),Predicate<T>等等,则在实例化委托时可以使用匿名函数。如
1 Action<int> a = delegate (int x) { Console.WriteLine(x); }; 2 Predicate<bool> b = delegate (bool x) { return x == true; }; 3 a.Invoke(1); 4 b.Invoke(1 == 1); 5 a(2); 6 b(2 == 3);
请注意匿名方法返回一个委托实例。如果只是单纯是只想处理一些无关于参数的事情,可以考虑省略参数。这让我想到一点就是在程序出错时,如果不记录参数,可以直接打印"出错了",而对于匿名函数,则可以省略参数。
1 System.Threading.Thread th = new System.Threading.Thread(delegate (object x) { }); 2 System.Threading.Thread th2 = new System.Threading.Thread(delegate() { });
其实上述的讲法是错的,其实很讨厌一些话,写了很多的东西,自己看得好像有点道理,然后突然告诉你"那些东西错误的观点",这次我也来使用一下。
前面说过匿名函数返回一个委托的实例,我解理为匿名方法确实是一个方法,因为存在之前说过的隐式转换(也就是C++中的复制构造函数,或者单参数的构造函数),它返回一个符合new System.Threading.Thread()中一个合适的参数类型,所以我觉得参数是不可省略的,书中就是可以省略,其实只是System.Threading.Thread有两个重载版本而已,自己的想法,错的请大牛们指正,小弟不胜感激。
请斧正。
- 2楼浩GE
- 另外楼主你看的书好像是只讲c# 2.0的?太旧了,建议看新一点的,c#都快出6.0了
- 1楼浩GE
- 引用Printer p = Print1调用了Printer类的构造函数,而+=操作应该是Printer类型重载了+操作,用于两个Printer类相加,说两个Printer类相加其实不太准确, 委托的+=操作其实是调用了Delegate的Combine方法(所有的委托都是继承Delegate的,Delegate的内部维护了一个Delegate数组,也就是委托链)。,引用Print2和Print3隐式转换为Printer类的实例,我觉得不应该称为隐式转换,称为语法糖或许更准确些,引用quot;我只是把一个方法给到了一个对象,让它帮我执行,我才不管你要让传什么样的类型quot;,说法不对,上面说了操作符+=其实是把委托对象“合并”了,也就是放进委托链里,最终调用时会迭代委托链依次执行。当然方法的签名需要和委托一致,引用前面说过匿名函数返回一个委托的实例,我解理为匿名方法确实是一个方法,匿名方法返回一个委托实例,这说法应该没错, 你针对什么委托类型使用匿名方法它就返回该委托类型的实例(签名需一致)。匿名方法确实是一个方法,这说法我个人觉得也对,因为你在针对委托使用匿名方法时,编译器内部其实会为你生成一个方法(必要时还会生成一个类,一般出现闭包什么的才会生成类),这个方法的实现就是匿名方法的实现,然后编译器根据该方法生成相应的委托返回。,我觉得你可以用ILDASM工具看看委托是怎么样的。也可以在代码中使用匿名方法编译然后再反编译生成代码,看看编译器帮你做了哪些事情,你应该会明白的更多。