一、前言

大家好!我是付工。

十年前,刚开始学C#编程的时候,被委托困扰了很久。

今天跟大家分享一下关于委托的那些事儿。

二、委托原理

什么是委托?

抛开编程,委托是一个汉语词语,指的是把事情托付给别人或别的机构办理。

为什么会有委托?什么时候使用委托?

有些事情我们直接干不了,需要找人来帮忙。

比如:

我们需要在主窗体中刷新子窗体的控件,

我们需要在多线程中刷新主线程的控件。

我们需要在某个窗体中执行另一个窗体的方法。

总之,当我们直接完成不了的时候,就可以考虑使用委托,如果可以直接完成,就没有委托什么事了。

三、委托案例

今天,我们结合一个案例来了解委托的前世今生。

这个错误应该每个人都遇到过,代码很简单,一运行就报错。

我们来分析一下错误提示:线程间操作无效:从不是创建控件“FrmMain”线程访问它。

这里的线程间意味着涉及两个线程,一个就是我们开的线程,另一个就是主线程。

从不是创建控件“FrmMain”线程,指的是什么线程呢?

我们知道程序运行之后,就会有一个主线程,又叫UI线程,通常用于处理用户界面相关的逻辑,如创建和显示窗体、处理用户输入、更新UI等。

所以,创建控件"FrmMain"的线程就是主线程。

不是创建控件“FrmMain”线程指的就是我们开的那个线程。

因此这个错误的意思就是不能在多线程里直接访问主线程的控件。

这个我们做牛马的应该都能理解,比如你花了半个月写了一个很牛的程序,你的同事想要"窃取",你是否愿意?

 

主线程辛辛苦苦创建了控件,多线程想直接给它赋值,主线程自然也不愿意,一怒之下,就给出了一个错误警告。

四、解决方案

我们回到这个问题上:你花了半个月写了一个很牛的程序,你的同事想要"窃取",你不愿意给,他怎么办呢?

于是,聪明的他开始搬救兵,找到了你的领导,表示他有一个类似的项目,利润很高,客户很急,需要借用你的程序参考一下。

于是,你的领导跟你画了一张饼,毋庸置疑,你同意了。

你的同事就是那个线程,而你就是主线程,你的领导就是委托。

我们再回到这个错误上来,既然直接不能访问主线程的控件,那么就采用"委托"来实现。

想要使用委托,必然要先学会委托。

在C#中,委托是一种类型,它定义了方法的签名,即方法的参数类型和返回值类型。

 

这句话如果没看明白,就不用管它了。

我们先来看看如何使用委托,这里总结了委托的五步法:

1、声明委托

声明委托需要根据最终执行方法来确定参数与返回值类型,然后根据参数和返回值来声明。

我们目的是给lbl_Time控件赋值当前时间,因此参数和返回值均为空。

声明委托代码如下

//【1】声明委托
public delegate void SetTimeDelegate();

2、创建委托对象

委托是一种类型,就像类class一样,我们都知道如果要创建某个类的对象的写法,那么创建委托对象是一样的。

创建委托对象代码如下:

//【2】创建委托对象
private SetTimeDelegate setTime;

3、创建委托方法

委托对象就像领导一样,它是不干活的,最终干活的还得是下面的牛马。

因此我们需要编写一个最终干活的方法,我们这个活很简单,所以委托方法也很简单。

创建委托方法代码如下:

//【3】创建委托方法
private void setTimeMethod()
{
this.lbl_Time.Text = DateTime.Now.ToString("HH:mm:ss");
}

4、委托绑定

领导有了,牛马有了,如何将这两者联系起来呢?

我们需要进行关系绑定,这就需要进行委托绑定。

委托绑定代码如下:

//【4】委托绑定
this.setTime = this.setTimeMethod;

5、委托调用

如果不涉及多线程,直接就像调用方法一样调用委托对象即可。

但是这里涉及到了多线程,也就是我们这里最终仍然需要主线程来调用。

怎么通过主线程来调用这个委托对象呢?

Control类中提供了一个Invoke方法,这个方法的含义是在拥有此控件的基础窗体句柄的线程上执行指定的委托。

委托调用的代码如下:

//多线程方法
private void TaskMethod()
{
//【5】调用委托
this.lbl_Time.Invoke(setTime);
}

这样就基于委托解决了跨线程访问的问题。

我们运行一下程序,效果如下:

这时候,我们在看这句话,是不是就豁然开朗了呢?

委托是一种类型,它定义了方法的签名,即方法的参数类型和返回值类型。

如果我们接触过C++编程,委托类似于C++中的指针。

 

五、委托今生

.Net Framework3.5之后开始有了Action和Func,Action和Func是内置委托,也叫系统委托,就是微软的工程师帮我们在底层写好了委托声明,这样我们就不需要声明委托。

Action委托针对无返回值情况,具有Action、Action<T>、Action<T1,T2>、Action<T1,T2,T3>……Action<T1,……T16>多达16个参数的形式,其中传入参数均采用泛型T,涵盖了几乎所有可能存在的无返回值的委托类型。

Func委托针对有返回值情况,具有Func<TResult>、Func<T,Tresult>……Func<T1,T2,T3……,Tresult>17种类型重载,T1……T16为参数,Tresult为返回类型。

于是我们开始简化我们的代码:

第一步简化:使用Action,不需要声明委托,创建的时候直接绑定

 //多线程方法
private void TaskMethod()
{
//创建并绑定
Action action = new Action(setTimeMethod);
//调用委托
this.lbl_Time.Invoke(action);
}

第二步简化:action对象只使用一次,直接调用即可

   //多线程方法
private void TaskMethod()
{
//创建委托、绑定委托、调用委托
this.lbl_Time.Invoke(new Action(setTimeMethod));
}

第三步简化:使用Lambda表达式替换委托方法

//多线程方法
private void TaskMethod()
{
//创建委托、委托方法、绑定委托、调用委托
this.lbl_Time.Invoke(new Action(()=>
{
this.lbl_Time.Text = DateTime.Now.ToString("HH:mm:ss");
}));
}

这个最终简化的代码是不是非常熟悉呢?

C#委托的前世今生的更多相关文章

  1. [C#] 回眸 C# 的前世今生 - 见证 C# 6.0 的新语法特性

    回眸 C# 的前世今生 - 见证 C# 6.0 的新语法特性 序 目前最新的版本是 C# 7.0,VS 的最新版本为 Visual Studio 2017 RC,两者都尚未进入正式阶段.C# 6.0 ...

  2. 浅谈.Net异步编程的前世今生----APM篇

    前言 在.Net程序开发过程中,我们经常会遇到如下场景: 编写WinForm程序客户端,需要查询数据库获取数据,于是我们根据需求写好了代码后,点击查询,发现界面卡死,无法响应.经过调试,发现查询数据库 ...

  3. Lambda的前世今生

    先看一段代码吧 class Student{ delegate void Say(string content); public void Show() { //Lambda的前世今生 //总结:La ...

  4. Facebook人工智能实验室的前世今生

    Facebook人工智能实验室的前世今生 是时候停止把Facebook当作纯粹的社交媒体公司来看了.它用无人机提供互联网服务,为了发展虚拟现实而收购Oculus,不懈追求人工智能,Facebook已经 ...

  5. Linq快速入门——Lambda表达式的前世今生

    Linq快速入门——Lambda表达式的前世今生   Lambda表达式其实并不陌生,他的前生就是匿名函数,所以要谈Lambda表达式,就不得不谈匿名函数,要谈匿名函数,那又要不得不谈委托. 何为委托 ...

  6. 浅谈.Net异步编程的前世今生----EAP篇

    前言 在上一篇博文中,我们提到了APM模型实现异步编程的模式,通过使用APM模型,可以简化.Net中编写异步程序的方式,但APM模型本身依然存在一些缺点,如无法得知操作进度,不能取消异步操作等. 针对 ...

  7. async & await 的前世今生(Updated)----代码demo

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  8. 无车承运前世今生,5G货运管家期待您的加入

    历时三年的无车承运人试点工作结束,从2020年1月1日起,将执行新的暂行<办法>,在这样一个承前启后的阶段,无车承运人的命运如何?网络货运经营者又是何物? 在新赛道下,将迎来什么样的机遇和 ...

  9. 【调侃】IOC前世今生

    前些天,参与了公司内部小组的一次技术交流,主要是针对<IOC与AOP>,本着学而时习之的态度及积极分享的精神,我就结合一个小故事来初浅地剖析一下我眼中的“IOC前世今生”,以方便初学者能更 ...

  10. async & await 的前世今生(Updated)

    async 和 await 出现在C# 5.0之后,给并行编程带来了不少的方便,特别是当在MVC中的Action也变成async之后,有点开始什么都是async的味道了.但是这也给我们编程埋下了一些隐 ...

随机推荐

  1. [TK] Tourist Attractions

    题目描述 给出一张有 \(n\) 个点 \(m\) 条边的无向图,每条边有边权. 你需要找一条从 \(1\) 到 \(n\) 的最短路径,并且这条路径在满足给出的 \(g\) 个限制的情况下可以在所有 ...

  2. LeetCode 332. Reconstruct Itinerary 最小欧拉路径

    题意 给N个单词表示N个点,和N-1个单词对,表示可以走的路径,求字典序最小的总路径. 首先说下这么暴力DFS能过.暴力的我都不敢写= = class Solution { public: vecto ...

  3. 使用 Ant-Design-Vue 制作一个带图片上传功能的表单对话框

    功能需求 使用 Antdv 的 Modal 组件内嵌一个 a-form 表单,具有添加数据和图片的功能. 页面结构设计 <template> <!--Modal--> < ...

  4. CentOS开放端口的方法(转载)

    CentOS开放端口的方法 转载:https://www.itfeichai.com/centos-open-porter/ Centos升级到7之后,内置的防火墙已经从iptables变成了fire ...

  5. gaussian噪声

    高斯噪声 高斯噪声(Gaussian noise)是一种具有正态分布(也称作高斯分布)概率密度函数的噪声.换句话说,高斯噪声的值遵循高斯分布或者它在各个频率分量上的能量具有高斯分布.它被极其普遍地应用 ...

  6. 2022年11月中国数据库排行榜:GaussDB获认证进前五,GBase得融资竞逐鹿

    击石乃有火,不击元无烟. 2022年11月的 墨天轮中国数据库流行度排行榜 火热出炉,本月共有246个数据库参与排名,相比上月新增1个数据库.由于国庆节长假,数据库整体热度指数有所降低,所以出现了多数 ...

  7. js模拟二维数组求和

    JavaScri实际上没有二维数组的概念,但是由于js变量是松散的,所以能设置数组元素为数组来模拟二维数组,以此类推,可以模拟多维数组. /* 需求:模拟了3 * 3数组求右上三角元素之和1 + 3 ...

  8. Vue的nextTick的原理

    知识储备:事件循环有宏任务和微任务,宏任务所处的队列就是宏任务队列,队列可以有多个,第一个队列只有一个任务就是执行主线程的js代码,剩余队列任务有setTimeout setInterval微任务所处 ...

  9. day04-常用DOS命令

    打开cmd的方式 开始-W-windows系统-命令提示符 win键+R键 鼠标在任意文件夹上, shift+鼠标右键 资源管理器的地址栏前面加cmd,然后回车 管理员方式运行:选择命令提示符右键以管 ...

  10. 警告:攻击者利用 SnoarQube 漏洞盗取国内多个机构的大量源码!

    2021 年 10 月 22 日,国外知名媒体 cybernews 发文称,有未知攻击者攻击并渗透了博世 iSite 的服务器,并盗取了这家制造业巨头的 5G 物联网连接平台的源代码. 攻击者声称通过 ...