C#委托的前世今生
一、前言
大家好!我是付工。
十年前,刚开始学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#委托的前世今生的更多相关文章
- [C#] 回眸 C# 的前世今生 - 见证 C# 6.0 的新语法特性
回眸 C# 的前世今生 - 见证 C# 6.0 的新语法特性 序 目前最新的版本是 C# 7.0,VS 的最新版本为 Visual Studio 2017 RC,两者都尚未进入正式阶段.C# 6.0 ...
- 浅谈.Net异步编程的前世今生----APM篇
前言 在.Net程序开发过程中,我们经常会遇到如下场景: 编写WinForm程序客户端,需要查询数据库获取数据,于是我们根据需求写好了代码后,点击查询,发现界面卡死,无法响应.经过调试,发现查询数据库 ...
- Lambda的前世今生
先看一段代码吧 class Student{ delegate void Say(string content); public void Show() { //Lambda的前世今生 //总结:La ...
- Facebook人工智能实验室的前世今生
Facebook人工智能实验室的前世今生 是时候停止把Facebook当作纯粹的社交媒体公司来看了.它用无人机提供互联网服务,为了发展虚拟现实而收购Oculus,不懈追求人工智能,Facebook已经 ...
- Linq快速入门——Lambda表达式的前世今生
Linq快速入门——Lambda表达式的前世今生 Lambda表达式其实并不陌生,他的前生就是匿名函数,所以要谈Lambda表达式,就不得不谈匿名函数,要谈匿名函数,那又要不得不谈委托. 何为委托 ...
- 浅谈.Net异步编程的前世今生----EAP篇
前言 在上一篇博文中,我们提到了APM模型实现异步编程的模式,通过使用APM模型,可以简化.Net中编写异步程序的方式,但APM模型本身依然存在一些缺点,如无法得知操作进度,不能取消异步操作等. 针对 ...
- async & await 的前世今生(Updated)----代码demo
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...
- 无车承运前世今生,5G货运管家期待您的加入
历时三年的无车承运人试点工作结束,从2020年1月1日起,将执行新的暂行<办法>,在这样一个承前启后的阶段,无车承运人的命运如何?网络货运经营者又是何物? 在新赛道下,将迎来什么样的机遇和 ...
- 【调侃】IOC前世今生
前些天,参与了公司内部小组的一次技术交流,主要是针对<IOC与AOP>,本着学而时习之的态度及积极分享的精神,我就结合一个小故事来初浅地剖析一下我眼中的“IOC前世今生”,以方便初学者能更 ...
- async & await 的前世今生(Updated)
async 和 await 出现在C# 5.0之后,给并行编程带来了不少的方便,特别是当在MVC中的Action也变成async之后,有点开始什么都是async的味道了.但是这也给我们编程埋下了一些隐 ...
随机推荐
- 痞子衡嵌入式:为下一代AI边缘处理设备而生 - i.MXRT700
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是恩智浦i.MX RTxxx系列MCU的新品i.MXRT700. 四年前恩智浦官宣了面向下一代智能穿戴设备的 i.MXRT500 系列,这 ...
- Spring技术书的代码资源下载
我是清华社编辑,这些资源获得作者授权,免费提供给读者个人学习使用.禁止任何形式的商用. 二维码用微信扫,按提示填写你的邮箱,转到电脑上打开邮箱下载.清华国企网盘,比较快速.安全.放心下载. 百度网盘链 ...
- Go语言中JSON标签的用法与技巧
在Go语言中,JSON标签(JSON tags)是用来指定结构体字段在序列化为JSON时的名称和行为的.JSON标签通常写在结构体字段的后面,用反引号(`)括起来.以下是一些常用的JSON标签: js ...
- NET Core 基础 - 删除字符串最后一个字符的七大类N种实现方式
今天想通过和大家分享如何删除字符串最后一个字符的N种实现方法,来回顾一些基础知识点. 01.第一类.字符串方式 这类方法是通过string类型自身方法直接实现. 1.Substring方法 相信大多数 ...
- 10月《中国数据库行业分析报告》已发布,深度剖析甲骨文大会Oracle技术新趋势
为了帮助大家及时了解中国数据库行业发展现状.梳理当前数据库市场环境和产品生态等情况,从2022年4月起,墨天轮社区行业分析研究团队出品将持续每月为大家推出最新<中国数据库行业分析报告>,持 ...
- ChatGPT “眼”中的开源数据库
开源作为数据库发展的未来趋势之一,被冠以"数据库弯道超车的法宝"的称号.中国开源数据库产品正处于蓬勃发展的趋势,根据 墨天轮中国数据库流行度 ,截止2023年2月底已有46款开源数 ...
- iOS长按手势列表拖拽功能实现
项目开发中遇到拖拽功能的需求,具体要求是在编辑状态下,首页底部菜单项可以拖动位置,便于位置切换.遇到问题后的初步想法是添加拖拽手势,拖拽到某个位置,判断拖拽cell的中心点是否在另一个cell内,这样 ...
- v-model 语法糖-在父子组件传值 的简写形式
props的变量名字 必须是 value ,this.$emit('input',数据值) 的自定义事件必须是 input : v-model 是 vue 中进行数据双向绑定的指令,在内部实际上是通 ...
- 09-react的组件传值 props
// 组件传值 props 接收传递过来的数据 import ReactDom from "react-dom" import { Component } from "r ...
- 怎么根据token的有⽆去控制路由的跳转?进度条跳转 - 白名单是否有token - 单独封装文件permission .js
vue这边的路由⾃带了路由前置守卫,我们可以在前置守卫⾥拿到token数据,然后根据需求做分⽀判 断,要是token存在就使⽤next⽅法正常放⾏跳转,否则可以强制跳回到登录,让⽤户去获取token ...