面试中可能会被问到的几个关于“委托”的问题
这几天比较清闲,刚好前段时间买了本CLR Via C#,在.Net界大家都知道Jeffrey大师的这本经典著作,惭愧一直没有拜读。
不过在C#著作中经典的非常多。读书的那时候就是通过《C#与.NET 3.0高级程序设计》这本书接触C#的,也被称为C#圣经。
CLR Via C#中的委托说的非常好,比较深入,让人耳目一新,回味无穷的感觉,看这本书的时候一定要记得带上ILDasm和
Reflector,有了他们就可以让我们更加深入的看一些问题,读书的方式有很多种,提问式的,提炼式的,因为任何东西都遵循
“八二原则“,100个字中,有营养的文字也就区区20个而已,抓住了就理解了,好了,就乱扯到这里了。
Q: 什么是委托
A: 委托就是一个继承自MulticastDelegate的类,不信可以用 ILdasm 看一下。
1 public delegate void FlyAction();
Q: 我从ILdasm中看到了Invoke方法,但是我在Delegate却没有看到该方法的定义?为什么?
A: 这其实是混淆了delegate关键字和FCL中的Delegate类型,而对于delegate关键字,编译器和CLR给我们做了很多的优化,
并且掩盖了很多复杂的细节,而FCL中的Delegate并没有。
Q: 我知道可以将方法作为参数传递给委托,然后可以在任何地方通过委托的invoke来执行那个作为参数的方法,请问是如何做到的?
A:既然可以在任何地方引用这个参数的方法,那就要看看方法是如何入侵到委托的。
① 先看看实例代码:
1 namespace Demo 2 { 3 class Program 4 { 5 public delegate void FlyAction(); 6 7 static void Main(string[] args) 8 { 9 Bird bird = new Bird(); 10 11 FlyAction action = new FlyAction(bird.Fly); 12 13 action.Invoke(); 14 } 15 } 16 17 public class Bird 18 { 19 public static Random rand = new Random(); 20 21 public void Fly() 22 { 23 return "i can fly " + rand.Next(); 24 } 25 } 26 }
从第一个QA中我们也看到了,其实委托是就是类,那我new一个类时,bird.fly其实就是类的构造函数的参数。
② 再来看看生成的IL中的构造函数。
这时候问题就出来了,为什么这里有两个参数,而new FlyAction(bird.Fly) 中却是一个参数,这里貌似是有问题的,不符合语法规
则,其实这里还是编译器提供了一个中间层,通过这个中间层做了一个转化,从而给我们隐藏了具体逻辑。其实在delegate中提供了
一个Target和Method属性,当我们传入bird.Fly时,Target记录了Bird这个类,Method记录了Bird中的Fly方法,所以invoke的时
候会自动触发Bird类中的Fly方法。证据如下:
这里补充一点,当new FlyAction的时候如果是静态方法,那么Target=null的,编译器只需要通过Method就能找到触发的方法。
Q:为什么在多播委托中,不建议回调方法有返回值,即使有返回值,多播中的方法也只能返回最后一个值,如果我想获取每个方法
的返回值,应该怎么处理? 实例代码如下:
1 class Program 2 { 3 public delegate string FlyAction(); 4 5 static void Main(string[] args) 6 { 7 Bird bird = new Bird(); 8 9 FlyAction action1 = new FlyAction(bird.Fly); 10 11 FlyAction action2 = new FlyAction(bird.Fly); 12 13 FlyAction action3 = new FlyAction(bird.Fly); 14 15 action1 += action2; 16 17 action1 += action3; 18 19 Console.WriteLine(action1.Invoke()); 20 21 var result = action1.GetInvocationList(); 22 23 Console.Read(); 24 } 25 } 26 27 public class Bird 28 { 29 public static Random rand = new Random(); 30 31 public string Fly() 32 { 33 return "i can fly " + rand.Next(); 34 } 35 }
A: 既然提到了“多播”,其实就是唬人了,内部源代码里面就是维护了一个List,将“多播”中的方法都放入到List中,Invoke的时候,
就循环遍历下List来依次调用里面的方法,这就是为什么建议不要用“有返回值“的方法。
下面我们可以通过 GetInvocationList 来获取这个list里面的方法。
然后我们再来看看这个GetInvocationList 里面的代码是怎么写的。
看到了this._invocationList和for循环,是不是有一种彻底明白的感觉,如果你想获取每个方法的返回值,那只能通过
GetInvocationList拿出来后,自己手工处理了,只有这样才能拿到“多播委托”中每个方法的返回值。
Q:请问下最后一个问题,问完就睡觉,请问委托可以动态创建吗?
A:可以的。Delegate中提供了CreateDelegate方法,就是可以动态创建的,举个例子你就知道了。
1 class Program 2 { 3 public delegate string FlyAction(); 4 5 static void Main(string[] args) 6 { 7 Bird bird = new Bird(); 8 9 //找到类下的方法 10 var method = typeof(Bird).GetMethod("Fly", BindingFlags.Instance | BindingFlags.Public); 11 12 var mydelegate = (FlyAction)Delegate.CreateDelegate(typeof(FlyAction), bird, method); 13 14 var result = mydelegate.Invoke(); 15 } 16 } 17 18 public class Bird 19 { 20 public string Fly() 21 { 22 return "i can fly " + new Random().Next(); 23 } 24 }
相关文章
- [Go] 理解计算机负数的表示以及整数范围
- [javascript]使用正则替换url中最后面的斜杠
- [PHP]面向对象多态性的体现
- [操作系统]内存页面置换算法
- 在线客服系统源码开发实战总结:gin框架模板渲染html页面
- 在线客服系统源码开发实战总结:H5 Notifications浏览器桌面通知
- wordpress独立站宝塔面板配置HTTPS教程
- 宝塔面板出现“require(): open_basedir restriction in effect. ”的解决方法
- elementui的loading加载页面的使用方式
- javascript监听页面关闭事件
- 【小程序】微信小程序开发在app.json中新建页面时报[ WXML 文件编译错误] (env: Windows,mp,1.06.2206090; lib: 2.25.0)
- 【Linux】宝塔面板设置MySQL慢查询日志,未走索引日志
- 客服系统搭建教程_宝塔面板下安装使用方式_可对接公众号_支持APP/h5多租户运营
- [宝塔面板] 客服系统适配宝塔面板,实现软件商店=>导入项目=>一键部署私有云在线客服系统
- [html] 同一页面的跳转,锚文本的使用方法
- [PHP] 解决宝塔面板运行php项目 pcntl_fork() has been disabled for security reasons
- 论文解读丨无参数的注意力模块SimAm
- 华为云企业级Redis揭秘第15期:Redis为什么需要强一致?
- 带你了解AKG正反向算子注册+关联流程
- 软件开发除了23种设计模式,还有7个开发原则需要了解