理解委托类型

怕大家看了觉得乱七八糟不知道讲什么,先一句话描述下:如标题,在C#中委托是一种类型,如class一样也需要声明,定义,赋值,并与class在同一个层面

再介绍之前先看下相关内容的定义(我不是咬文嚼字,个人以为,有些东西的定义一定要没明白,不能含糊,不然只会是似懂非懂)
delegate :(关键字->类型->引用类型[class,interface等等这些都是引用类型关键字])

delegate 是一种可用于封装命名或匿名方法的引用类型。(并不是所有的引用类型都是class)

是一种定义方法签名的类型,可以与具有兼容签名的任何方法关联。

与委托的签名(由返回类型和参数组成)匹配的任何可访问类或结构中的任何方法都可以分配给该委托。

event:(关键字->修饰符[readonly,static,unsafe等等这些都是修饰符关键字])
                event    关键字用于在发行者类中声明事件。  (所以被event修饰的多路广播委托[MulticastDelegate]才被称“事件”[这个事件并不是关      	        键字event,而是MulticastDelegate的一个特殊实例])

Delegate (类 -> System命名空间下 [程序集mscorlib])
                 
MulticastDelegate(类-> System命名空间下 [程序集mscorlib])
                 

EventHandler(委托 [他不是类,是委托])

实际上是一种.NET为我们预定义委托类型,后面会将到

现在来看看Delegate 里面是什么

可以清楚的看到Delegate 确实就是一个类,而且直接继承于Object(这个继承是IL层面的,C#层面是看不到的所以不用太在意)

里面有很多方法,不过我们可能能使用的也只有public的方法而已,而且这是一个抽象类我们也是无法直接实例化的(下面的同样也是)

再来看看MulticastDelegate里面是什么

很明显MulticastDelegate同样是个类而且直接继承与上面的Delegate

还多了2个私有字段 _invocationCount , _invocationList ,以及一些新的方法

由此可以看出这MulticastDelegate,跟Delegate,虽然名字很有迷惑性,但是他们不是委托,是class(下面会提到这个class跟我们委托之间的关系),而且正常情况下我们还用不到,不过经常有人把Delegate跟delegate混淆(完全不是一个东西,跟string,String可不一样)

可以看下EventHandler里面是什么



可以看到里面就是一个声明了一委托(这跟声明一个类是类似的),他是声明了一个delegate类型的数据,所以EventHandler就很清楚了就是一个委托类型的数据(只是声明而已,而delegate的声明是可以放在命名空间顶层跟其他数据结构的声明享受同意的待遇的)

(不过IL代码里依然用class修饰,我个人认为IL里面的class是IL的class,C#里面的class是C#的class,是不一样的东西。不能因为IL里面是用class修饰就一厢情愿的说这个东西是类,如果是这样Interface,enum,struck什么的也都是类了,最后他们都是2进制的数据,都是一样的东西)


 

现在让我们看看所谓委托的整个过程

第一步:  我们先定义/声明一个委托(注意是定义)

public delegate void delegateLoopChangeEventHandler(object yourTarget, string yourMessage);

就像这样(不能定义在接口或函数中,但是可以在struct里面定义)

我们看看CLR是怎么处理这句话的

可以很明白的看到他真的是一个委托那个公文包一样的小图标就是委托的意思

打开它看看里面有什么

可以看到里面有1个public 方法,跟3个好像跟方法和类似的东西(因为这2个框框组成的图标是什么含义我确实也不清楚)

可见委托里面也是可以有方法的(不过这些方法不是我们自己创建的,其实我们定义一个委托类型的数据体的时候就是一局话,而IDE为我们转换为IL的时候其实是有很多操作的)

我们来看下这一句的代码编译器是怎么理解的,IDE把它翻译成了什么

看到了吧,转化为IL代码的时候多了一些东西,在IL层面它是IL的class(一定要分清是IL的class,不是C#语法里的class)并且继承于MulticastDelegate

再次说明下这个delegateLoopChangeEventHandler 是一个委托,在IL里表现为class,而这个class跟C#里面的类,委托,接口不是一个环境的东西

第二步:我们要实例化这个委托(这里的操作类似于  Point myPt; FileStream myFs 这样)

public static delegateLoopChangeEventHandler OnLoopChange;

我是在类里实例化的,所以可以加上修饰符(测试中为了直接给main函数使用,所以才加了个static,实际环境还是建议少用静态的东西)。

然后我们可以看到这一句的IL代码也只有一句

当程序运行到此处的时候 OnLoopChange  的值是null,也就是说我们还没有对这个委托类型的数据进行初始化/赋值

第三步:我们来赋值

eg 1 :  OnLoopChange = new delegateLoopChangeEventHandler((x, y) => { Console.WriteLine("myIndex" + myIndex++); });

eg 2 :  OnLoopChange += new delegateLoopChangeEventHandler((x, y) => { Console.WriteLine("myIndex" + myIndex++); });

eg 3 :  OnLoopChange =((x, y) => { Console.WriteLine("myIndex EX"); });

eg 4 :  OnLoopChange = new delegateLoopChangeEventHandler(FunTest1);

可以看到赋值的操作的写法有很多,可以很随意,当然推荐最后一种,可以随时删除绑定的方法(当然不是说Lambda 表达式不好,除非你很清楚你的方法就只是要做1,2行代码的事情)

这个赋值没有什么好说的与其他类型的数据赋值也是一样的,还是来看下clr的il(随便找的一个委托类型的初始化,跟上面的不是一起的)

其实对IL不是很熟悉,简单说下,每当我们要赋值/初始化他大概是先把我们要绑定的函数移到堆栈,然后他是新建了个delegateLoopChange类型的数据(因为我们刚刚说了只是实例化后我们的委托实例是为null的),最后用堆栈里的这些数据替换了我们刚刚为null的委托字段。

再说明下 我们可以OnLoopChange =((x, y) => { Console.WriteLine("myIndex EX"); });这样直接赋值,是因为IDE会帮我们加上new delegateLoopChangeEventHandler(......),不熟悉的最好不要偷懒

然后委托实例的赋值我们可以使用+=,不过这个+=对null的实例于非null实例处理可不一样

如果为null就是上面的初始化过程。

如果不是null,他实际进行的操作是

简单的说就是它向我们的实例又添加了一个方法。(应该很容易想到相应的会有-=)。

当然我们也可以在实例化的时候同时给他赋值,也就是说第2步跟第,3步可以一起来做。

第四步:执行委托

执行就非常简单了,刚刚的委托直接OnLoopChange (null,null);操作起来就像是调用一个方法一样

也可以OnLoopChange .Invoke(null, null);

特别说明下这个Invoke不是System.Windows.Forms里Control类的Invoke,这里他是DomainOperationEntry下的

然后如果当前委托实例如果绑定了多个方法,都会逐个执行

现在顺便说一下Control下的Invoke

当然哪线程个执行OnLoopChange,OnLoopChange就在哪个线程上面执行,有的时候你有可能会操作UI,当是当前又不是UI线程,这个时候怎么办,的确我们可以关闭VS的UI线程检查一了百了,不过这肯定是不推荐的,首先很难保证不会有同时访问的情况,其次操作ui一般都很耗时

现在我们就可以使用Control下的Invoke。

方法也很简单 myFormControl1.Invoke(myFormControl1.myDelegate,new Object[] {myString});   就这么一句就可以了(这个我直接摘的MSDN的)

简单的说就是  控件.Invoke(委托实例):   如果有参数直接加在后面就行了

说明下 Invoke给UI虽然是UI线程执行 不过当前函数会等待方法返回。

使用BeginInvoke可以解决

最后委托实例用完了怎么处理,一般出于习惯会-=,不过MSDN上也没有提交这个东西需要特别的销毁或释放,应该是被托管的,我们不用管

好了再来说一下"事件"(其实明白委托后,就不会在纠结事件跟委托的关系了)

MSDN上已经描述的非常清楚精准了

“事件是特殊类型的多路广播委托,仅可从声明它们的类或结构(发行者类)中调用”

所以很明白了事件就是委托,被一个关键字event修饰过的委托而已。(就好像 static int i =1 ; 你会说i不是int型数据么)

如果非要找区别,MSDN也说了,他只是不能在其他类中调用而已。

其实event就是微软多帮我们实现了写功能。

来分析他说的这句十分精确的话“仅可从声明它们的类或结构(发行者类)中调用”

乍一看不是很简单实现吗,吧这个委托的示例用private 来修饰不就可以了(事实上VS在遇到event的时候也是这么处理的)

不过不要忘了委托的功能,其他类会订阅这个问题(其实就是赋值或添加关联函数)

这样一来是不是很麻烦了,已经是private了 ,其他类肯定不能处理了,我们是不是要单独写函数暴露出去给调用方去使用 等等。

好了不用我们处理了,微软加个event修饰符帮我们解决

来看下微软是怎么做的

可以看到LoopChangeEvent这个特殊委托实例真的是用private在修饰,不过他在内部帮我们添加了2个方法,跟这么一个所谓的事件(其实2个静态方法是在事件里面的,也不知道它为什么显示在了同级),其实这个事件就是为了给这个private委托实例开放2个函数。

既然事件就是委托,使用方法也是完全一样的,也不重复写了(唯一的区别是“仅可从声明它们的类或结构(发行者类)中调用”)

值得注意的是即时被event修饰,如果把当前委托实例传给其他类,在其他类中也是可以触发委托(事件)的,只是不能直接把这个委托当作类的字段去使用(触发)

现在再回到前面的EventHandler ,应该很清楚了吧

其实他就是委托类型的数据的声明(存在的意义同样也只是为了方便我们或者说是规范我们)

MSDN上就很直白的写着

[SerializableAttribute] [ComVisibleAttribute(true)]

public delegate void EventHandler(Object sender,EventArgs e)

这不就是前面提到的委托的声明方法一样。所以他就是委托,微软帮我们预先声明了几种委托而已

EventHandler等等里面有event这个单词存在,是在提醒我们,这种委托类型的实例最好用event来修饰

最后总结下

如果要问事件是不是委托。

那必须先搞清楚这到底是在问什么

是要问event 跟 delegate  还是 Delegate  的区别

这些开头就有解释,根本不是一个类型的东西,绝对不一样。

那如果是问委托实例,跟事件实例呢

MSDN也说的很清楚了。事件是特殊类型的多路广播委托

如果要问委托是不是类

在C#委托绝对不会是类,要不然要delegate这个关键字干什么直接用class。

不过dclass跟delegate被翻译成IL的时候就都是属于IL的class的。

IL终究不是C#,不会影响结论:在C#委托 不是 类

委托的一般应用

跨类跨线程的消息通知(委托的执行是定义委托的类或线程,委托的赋值是其他类或线程,在没有invoke的情况下调用线程是执行线程,这种问题一般加事件修饰)

将委托传递给其他方法,让其他线程,类,方法去调用(这种调用方一般只调用,赋值也是其他模块做的,这也是通常理解的函数指针)

东西写的很乱,可以也有很多不完善或者有错误的地方。看起来肯定也很吃力,见谅,存在错误的地方请多多指正

参考文献:MSDN ,<一站式示例代码库编程规范>

合作编辑:无

时间: 2024-10-07 10:17:25

理解委托类型的相关文章

进一步理解委托

前面一篇文章介绍了委托的基本知识,接下来就进一步研究一下委托. 委托类型 其实,刚开始觉得委托类型是一个比较难理解的概念,怎么也不觉得下面的"AssembleIphoneHandler"是一个类型. public delegate void AssembleIphoneHandler(); 按照正常的情况,如果我们要创建一个委托类型应该是: public class AssembleIphoneHandler : System.MulticastDelegate { } 但是,这种写法

理解委托(delegate)及为什么要使用委托

理解委托(delegate)及为什么要使用委托 委托:是一种定义方法签名的类型. 当实例化委托时,您可以将其实例与任何具有兼容签名的方法相关联. 您可以通过委托实例调用方法. 上述为官方说法,理解起来比较难,举个生活中的例子: 某人有三子,让他们各自带一样东西出门,并带回一头猎物.上面一句话可以理解为父亲对儿子的委托:猎物 办法(工具 某工具)-->delegate 猎物(返回值) 带回猎物(委托名)(工具(参数类型) x)-->delegate int GetValue(int i)三个人执

C#学习之初步理解委托、事件、匿名方法和Lambda

最经在学习LinqtoSql,然后扯到Lambda表达式,然后扯到匿名方法,然后扯到委托,最后扯到事件处理...后来发现对委托这个概念和事件处理这个过程理解得不是很清晰,遂得一下学习笔记.那里说得不对,请大家多多指教! 第一部分:理解委托 委托委托,顾名思义,就是类似于中间人的意思,有些事情你不做,委托别人去做,比如你想相亲,但你不想去主动约女孩子,那你可以交给媒婆去帮你约. 如果你学过C++,请把委托理解成函数指针,都是为了调用函数.函数指针可以调用符合该函数指针要求的函数.什么叫符合该函数指

使用委托中介租房理解委托与事件

委托和事件,.Net Framework中的应用非常广泛,然而,较好的理解委托和事件对很多接触C#时间不长的人来说并不容易.它们就像一道门槛儿,跨过去的,觉得太容易了,而没有过去的人每次见到委托事件就觉得心慌慌,浑身不自在. 我个人还是比较喜欢用面向对象的编程思想去理解逻辑程序,理解编程.下面就用委托中介公司租房子的示例理解使用委托流程: 1.定义委托和委托对象——租房人想要租房,但是没有房源,于是委托中介找房子:2.编写要委托的方法——租房中介有房源,可以找到房子:3.将要委托的方法传递给委托

[转] 把委托说透(2):深入理解委托

在上一篇随笔中我们通过示例逐步引入了委托,并比较了委托和接口.本文将重点剖析委托的实质. 委托在本质上仍然是一个类,我们用 delegate 关键字声明的所有委托都继承自System.MulticastDelegate.后者又是继承自System.Delegate类,System.Delegate类则继承自System.Object.委托既然是一个类,那么它就可以被定义在任何地方,即可以定义在类的内部,也可以定义在类的外部. 正如很多资料上所说的,委托是一种类型安全的函数回调机制, 它不仅能够调

C#委托的定义 以及使用方式详解,更简单的理解委托。

委托的声明及定义: 委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-Else(Switch)语句,同时使得程序具有更好的可扩展性.“ 这是百度上和许多教科书上都有的一个概念,但是初学这门编程语言的话,或者基础比较差的童鞋,看到这句话,肯定是一脸懵比,先不用着急,我们来慢慢通过代码去理解委托这个概念. 下面就在C#控制台上看代码把 相信刚学C#的朋友们应该很熟悉 using System;using Sy

编写高质量代码改善C#程序的157个建议——建议44:理解委托中的协变

建议44:理解委托中的协变 委托中的泛型变量天然是部分支持协变的.为什么是“部分支持协变”?看下面示例: class Program { public delegate T GetEmployeeHanlder<T>(string name); static void Main() { GetEmployeeHanlder<Employee> getAEmployee = GetAManager; Employee e = getAEmployee("Mike"

自定义委托类型 - .Net自带委托类型

委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递. 与其他的类不同,委托类具有一个签名,并且它只能对与其签名匹配的方法进行引用. 一.自定义委托类型 1.语法结构:访问修饰符 delegate 返回类型 委托类型名称(参数列表); 例如: // 声明一个委托类型,两个参数均为int类型,返回值为int类型 public delegate int Calc(int a, int b);自定义的委托可以不带参数,也可以没有返回值. 接下来我们看一个例子怎么使用委托 1.

《Effective Modern C++》翻译--条款1: 理解模板类型推导

北京2016年1月9日13:47:17 开始第一章的翻译. 第一章名为 类型推断 分为四个条款: 1理解模板类型推导 2理解auto自动类型推导 3理解decltype操作符 4如何对待推导的类型 第一章 类型推导 C++98有一套单一的类型推导的规则用来推导函数模板.C++11轻微的修改了这些规则并且增加了两个推导规则,一个用于auto,一个用于decltype.接着C++14扩展了auto和decltype可以使用的语境.类型推导的普遍应用将程序员从必须拼写那些显然多余的类型中解放了出来,它