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的味道了.但是这也给我们编程埋下了一些隐 ...
随机推荐
- JAVAEE——tomcat安装
一.下载 1.网址:Tomcat官网 选择版本:点击左边Download下的which version,根据Supported Java Versions来选择合适的Tomcat版本 下载软件:点击左 ...
- BOOST库array使用 类似std库的vector
BOOST库的array, 类似std库的vector. 下图所示书籍的下载地址,我的另一篇博客内有记载: https://www.cnblogs.com/happybirthdaytoyou/p/ ...
- C#|.net core 基础 - 深拷贝的五大类N种实现方式
在实际应用中经常会有这样的需求:获取一个与原对象数据相同但是独立于原对象的精准副本,简单来说就是克隆一份,拷贝一份,复制一份和原对象一样的对象,但是两者各种修改不能互相影响.这一行为也叫深克隆,深拷贝 ...
- Qt构建cmake工程方法总结
由于工作需要,最近打算统一将所有C/C++项目都改成使用cmake编译.传统后台业务问题不大,但是有些牵涉到跨平台的Qt项目还是折腾了一阵.下面对这段时间的收获做一个总结,也希望帮助看到本文的朋友少走 ...
- urb中几个函数的使用
usb_buffer_alloc(free) 说是为了更好的从名字看出这个函数真实做的事情:DMA coherency linux提供两种方式,来保证使用dma时,内存和硬件cache的一致性: us ...
- electron 菜单选项 - 隐藏,设置菜单
隐藏菜单 const { app, Menu, session } = require('electron'); /*隐藏electron的菜单栏*/ Menu.setApplicationMenu( ...
- JDBC后端实现登录的逻辑
// 包名 package com.zhulx.JDBC; // 导入实例类 import com.zhulx.pojo.Account; import java.sql.Connection; im ...
- 000 通过 Pytorch 实现 Transformer 框架完整代码(带注释)
博客配套视频链接: https://space.bilibili.com/383551518?spm_id_from=333.1007.0.0 b 站直接看 配套 github 链接:https:// ...
- 9.24 csp(没学会的网络流)
T1.商品 因为边界 l , r 是线性移动的,所以答案可以线性改变,直接用set维护连续段(小于l的和大于r的)的个数,并维护ans即可. 因为set的一个小错误调了两个小时,代码打成了一坨,结果最 ...
- 使用notepad++批量在每行首尾添加内容
1 简介 在程序员开发过程中,一个不错的工具是notepad++,该工具为notepad的增强,增强了许多的功能,包括程序员喜欢的列块编辑模式,支持众多的插件,例如json格式化,支持markdown ...