委托的使用与原理简析

简介: 一.委托声明与本质1.声明委托public delegate void SayHelloDelegate(string who);2.

一.委托声明与本质

1.声明委托

public delegate void SayHelloDelegate(string who);

2.使用ILSpy反编译后,看其本质

public class auto ansi sealed SayHelloDelegate: MulticastDelegate
  • 编译器自动生成了一个委托类,继承自MulticastDelegate

这里写图片描述

  • 委托被标识为class,说明委托是一种数据类型:

    委托类即可嵌套在一个类型中定义,也可以在全局范围中定义,就是说由于委托是类,所以凡是能够定义类的地方,都能定义委托;当然也可以声明委托类的变量。

  • 委托被标识味sealed,说明是封闭类,不可以被继承

二.创建委托变量与本质

1.既然委托是类,并且不是静态类,想要使用,就必须new

//委托声明
public delegate void SayHelloDelegate(string who);

static void Main(string[] args)
{
    //创建委托变量(使用new关键字)
    SayHelloDelegate sayDel1 = new SayHelloDelegate(SayHelloToAmerican);
    //创建委托变量(不使用new关键字)
    SayHelloDelegate sayDel2 = SayHelloToAmerican;
}

//类的静态方法
static void SayHelloToAmerican(string who)
{
    Console.WriteLine("hello:" + who);
}

2.反编译看两种创建变量的方式,其本质是一样的

private static void Main(string[] args)
{
    Program.SayHelloDelegate sayDel1 = new Program.SayHelloDelegate(Program.SayHelloToAmerican);
    Program.SayHelloDelegate sayDel2 = new Program.SayHelloDelegate(Program.SayHelloToAmerican);
    Console.ReadLine();
}

编译器都会给搞成new,所以不带new的变量创建方式,是语法糖。

三.调用委托变量与本质

1.给委托变量加上()就相当于调用了

static void Main(string[] args)
{
    //创建委托变量(使用new关键字)
    SayHelloDelegate sayDel = new SayHelloDelegate(SayHelloToAmerican);

    //委托变量调用
    sayDel("jack");
}

2.反编译看其本质是调用了Invoke方法
所以我们自己也可以直接调用这个方法,委托变量调用的这两种方法,本质是一样的,编译器都会调用Invoke。简写方式也是语法糖。

sayDel("jack");
sayDel.Invoke("jack");

3.委托变量的调用本质上通过反射对方法的调用
这是委托的构造函数,有两个参数

public SayHelloDelegate(object target, string method)
{
}

委托类同时提供了两个只读属性TargetMethod供使用,是将委托变量指向的对象方法进行了包装,如果方法是静态方法,则Target为null,否则就指向对象的引用。Method属性返回一个System.Reflection.MethodInfo对象的引用。在我们调用Invoke方法时,其实是执行了委托变量指向的方法,只不过有一个内部包装和调用的机制。

四.委托的好处

1.逻辑分离、解除耦合
业务逻辑未来可能会变化,不能固定,分离处理;只要把委托给我,让我调用就可以了。
2.对修改封闭、对扩展开放
未来可以给我传任何一个代理,这就是扩展。
而我不需要修改任何代码,这就是封闭。
我省事,你随意。我好你也好。
这里写图片描述

五.委托的异步调用

反编译看到委托还有两个方法:

public IAsyncResult BeginInvoke(string who,   AsyncCallback callback, object @object)
{           
}

public void EndInvoke(IAsyncResult result)
{
}

1.异步调用的本质和适合场景
这是异步调用方式,本质上是建立了一个线程,是简化的线程调用方法。

比较适合在后台运行比较耗费时间的简单任务,要求任务之间是独立的、建议任务中不有要直接访问可视化控件的逻辑
如果后台任务必须按照特定顺序执行,或者需要访问共享资源,异步编程不太适合,直接用多线程开发技术。

这里写图片描述

2.BeginInvoke跟Invoke的区别
调用Invoke,在Invoke的方法返回前,这个线程会阻塞;调用BeginInvoke,在BeginInvoke的方法返回前,这个线程不会阻塞!

六.多播委托与泛型委托

1.泛型委托变量指向多个方法并调用
委托可以声明为泛型的,一个委托变量可以指向多个方法。

private delegate string AddDelegate<T>(T i, T j);

private string MyAdd(int i, int j)
{
    Console.WriteLine(i + j + "");

    return i + j + "";
}

private void button2_Click(object sender, EventArgs e)
{
    AddDelegate<int> add = MyAdd;
    add += MyAdd;
    add += MyAdd;

    foreach (AddDelegate<int> item in add.GetInvocationList())
    {
        item(3, 4);
    }
}

add委托变量指向了一个委托方法链,如果使用add()直接调用,委托方法链中的每个方法都会调用,但是返回值只能是最后一个方法。
2.反编译看起本质

add = (AddDelegate<int>)Delegate.Combine(add, new AddDelegate<int>(this.MyAdd));
add = (AddDelegate<int>)Delegate.Combine(add, new AddDelegate<int>(this.MyAdd));

调用了基类Delegate的Combine方法加入了方法链。

七.委托与匿名方法

1.使用匿名方法简化代码
如果委托指向的方法只是临时用一下,明确声明一个方法就显得啰嗦了,这个时候可以使用匿名方法

private delegate void SayHelloHandler(string name);

private void SayHelloToSomeone(string name, SayHelloHandler sayHandler)
{
    sayHandler(name);
}

private void SayHelloTest()
{          
    //标准匿名方法
    SayHelloHandler sayAmerican = new SayHelloHandler(delegate (string name) { Console.WriteLine("你好:" + name); });
    SayHelloToSomeone("jack", sayAmerican);

    //省略new,编译器会自动加上new
    SayHelloHandler sayAmerican1 = delegate (string name) { Console.WriteLine("你好:" + name); }; 
    SayHelloToSomeone("jack", sayAmerican);

    //可以简化为lambda表达式
    SayHelloHandler sayChinese = name => Console.WriteLine("你好:" + name);
    SayHelloToSomeone("李老师", sayChinese);
}

private void button3_Click(object sender, EventArgs e)
{
    SayHelloTest();
}

2.lambda的本质是匿名方法
反编译看,lambda表达式本质上是匿名方法,编译器自动生成了这个方法。

private sealed class <>c
{   
    internal void <SayHelloTest>b__9_0(string name)
    {
        Console.WriteLine("hello:" + name);
    }

    internal void <SayHelloTest>b__9_1(string name)
    {
        Console.WriteLine("你好:" + name);
    }
}

3.委托的严格限制
方法的参数和返回值必须和委托签名保持一致。可以省略lambda的参数类型,编译器将自动推断。

//可以简化为lambda表达式
SayHelloHandler sayChinese = name => Console.WriteLine("你好:" + name);
相关文章
|
虚拟化
故障案例-ESXI6.7 EP13 紫屏分析
一台ESXI6.7 EP13 紫屏分析过程
2749 0
|
存储 机器学习/深度学习 算法
蓝桥杯练习题(三):Python组之算法训练提高综合五十题
蓝桥杯Python编程练习题的集合,涵盖了从基础到提高的多个算法题目及其解答。
541 3
蓝桥杯练习题(三):Python组之算法训练提高综合五十题
|
11月前
|
Web App开发 前端开发 JavaScript
前端开发必备神器大公开,用过的人都哭了:效率翻倍不是梦!
前端开发结合了创意与技术,本文介绍了几个提升开发效率的工具:Visual Studio Code、Webpack、Postman、GitHub 和 Chrome DevTools。这些工具分别在代码编辑、模块打包、API 测试、版本控制和网页调试等方面发挥重要作用,帮助开发者提高工作效率,优化项目管理。
218 4
|
算法 vr&ar C#
使用Unity进行虚拟现实开发:深入探索与实践
【8月更文挑战第24天】使用Unity进行虚拟现实开发是一个充满挑战和机遇的过程。通过掌握Unity的VR开发技术,你可以创造出令人惊叹的VR体验,为用户带来前所未有的沉浸感和乐趣。随着技术的不断进步和应用场景的不断拓展,VR开发的未来充满了无限可能。希望本文能为你提供有用的指导和启发!
|
数据可视化 Python Windows
Matplotlib输出中文显示的2种解决方案
Matplotlib输出中文显示的2种解决方案
593 1
|
NoSQL MongoDB Windows
MongoDB 读写分离——Windows MongoDB 副本集配置
MongoDB 读写分离——Windows MongoDB 副本集配置
332 0
|
移动开发 JavaScript
微信公众号H5开发,在微信浏览器打开H5,无法一键下载图片
微信公众号H5开发,在微信浏览器打开H5,无法一键下载图片
709 0
|
存储 SQL 设计模式
C++面试题,阿里、百度、腾讯、华为、小米100道C++面试题目及答案
C++面试题,阿里、百度、腾讯、华为、小米100道C++面试题目及答案
1187 0
|
Java 调度 Spring
深入解析 Java 中的 @Service 注解:优化业务逻辑的组织与管理
在现代软件开发中,良好的代码组织和业务逻辑的模块化是保持代码可维护性和可扩展性的重要因素。而 Java 中的 `@Service` 注解则是实现业务逻辑组织和管理的有力工具。通过该注解,我们可以将业务逻辑标记为服务组件,实现依赖注入和松耦合的架构。本文将带您深入探索 Java 中的 `@Service` 注解,揭示其作用、用法以及在实际开发中的应用场景。
|
负载均衡 Java Nacos
【java常见的面试题】Spring Cloud 5大组件有哪些?
Java基础的面试题【SpringBoot&SpringCloud篇】
1413 0