一、前言

大家好!我是付工。

十年前,刚开始学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. ASP.NET Core – 操作 Uri 和 Query

    前言 以前就有写过了 Asp.net core 学习笔记 (操作 URL 和 Query), 但很乱, 这篇作为整理. Uri 介绍 结构: [Scheme]://[Host]:[Port][/Pat ...

  2. Hugging Face 论文平台 Daily Papers 功能全解析

    文/ Adeena, 在快速发展的研究领域,保持对最新进展的关注至关重要.为了帮助开发者和研究人员跟踪 AI 领域的前沿动态,Hugging Face 推出了 Daily Papers 页面.自发布以 ...

  3. MQ核心作用异步&削峰&解耦使用场景详解

    说在前面 在如今的高并发互联网应用中,如何确保系统在巨大的流量冲击下还能稳稳当当运转,是每个技术团队都会遇到的挑战.说到这,消息队列(MQ) 就是背后的"大功臣"了. 无论是异步处 ...

  4. iOS多态使用小结

    多态是面试程序设计(OOP)一个重要特征,但在iOS中,可能比较少的人会留意这个特征,实际上在开发中我们可能已经不经意的使用了多态.比如说: 有一个tableView,它有多种cell,cell的UI ...

  5. 41.key发生变化,dom还会复用吗

    会复用但是可能会产生没有必要的真实DOM更新  ,会降低渲染效率 :比如使用 index 作为 key 值 :

  6. Python之爬虫-全民k歌

    import re import os import requests from aip import AipSpeech from pydub import AudioSegment APP_ID ...

  7. 欢迎来到IoT解忧杂货铺

    这是一间特殊的杂货铺 门面不大,却包罗万物 如果你也遇到一些烦恼 欢迎来到,IoT解忧杂货铺 解忧秘方·工业 厂里的几十台设备真让人头疼 协议种类太多太复杂 设备没法全联网 产线故障了也不知道 自己出 ...

  8. MongoDB mongod.log "connection refused because too many open connections" 处理方法

    一.MongoDB副本集 副本集名称 角色 IP地址 端口号 优先级 CCTV-test Primary 192.168.1.21 27017 10 Secondary 192.168.1.21 27 ...

  9. Vulhub 安装运行

    前言 vulhub是利用docker技术做的一个漏洞复现平台,可以一键搭建对应的配置.在下载好对应的代码包后,不需要安装,只需要解压并利用3条命令,就可以简单的创建关闭对应漏洞环境.最好是购买一台阿里 ...

  10. redis的CPA三进二原则

    CAP C:consistency,数据在多个副本中能保持一致的状态. A:Availability,整个系统在任何时刻都能提供可用的服务,通常达到99.99%四个九可以称为高可用 P:Partiti ...