C#中委托的进一步理解

 更新时间:2015年02月26日 10:09:20   投稿:junjie  
这篇文章主要介绍了C#中委托的进一步理解,本文讲解了委托类型、建立委托链、移除委托链等内容,需要的朋友可以参考下

前面一篇文章介绍了委托的基本知识,接下来就进一步研究一下委托。

委托类型

其实,刚开始觉得委托类型是一个比较难理解的概念,怎么也不觉得下面的"AssembleIphoneHandler"是一个类型。

复制代码 代码如下:

public delegate void AssembleIphoneHandler();

按照正常的情况,如果我们要创建一个委托类型应该是:
复制代码 代码如下:

public class AssembleIphoneHandler : System.MulticastDelegate
{
}

但是,这种写法是编译不过的,会提示不能从"System.MulticastDelegate"派生子类。

其实,这里是编译器为我们做了一个转换,当我们使用delegate关键字声明一个委托类型的时候,编译器就会按照上面代码片段中的方式为我们创建一个委托类型。

知道了这些东西,对于委托类型的理解就比较容易了,通过delegate声明的委托类型就是一个从"System.MulticastDelegate"派生出来的子类。

建立委托链

下面我们通过一个例子来看看委托链的建立,以及调用列表的变化,基于前面一篇文章中的例子进行一些修改。

复制代码 代码如下:

class Program
{
    static void Main(string[] args)
    {
        Apple apple = new Apple();
        Foxconn foxconn = new Foxconn();

        Apple.AssembleIphoneHandler d1, d2, d3, d4 = null;
        d1 = new Apple.AssembleIphoneHandler(foxconn.AssembleIphone);
        d2 = new Apple.AssembleIphoneHandler(foxconn.PackIphone);
        d3 = new Apple.AssembleIphoneHandler(foxconn.ShipIphone);

        d4 += d1;
        d4 += d2;
        d4 += d3;

        d4();

        Console.Read();
    }
}

我们接下来进行一下单步调试看看委托链建立的过程。

1. 当下面三句执行完成后,可以通过VS看到d1、d2和d3的详细信息

复制代码 代码如下:

d1 = new Apple.AssembleIphoneHandler(foxconn.AssembleIphone);

复制代码 代码如下:

d2 = new Apple.AssembleIphoneHandler(foxconn.PackIphone);

复制代码 代码如下:

d3 = new Apple.AssembleIphoneHandler(foxconn.ShipIphone);

对于上面三个委托实例来说:

1.调用列表为空,所以_invocationCount为0,_invocationList为空
2._target代表创建委托实例的方法来自Foxconn的实例;如果是静态方法创建的委托实例_target值为null
3._methodPtr代表这个方法的唯一标识,可以理解为句柄
4._methodBase包含创建委托实例的方法的信息,方法名、返回类型等等

2. 通过"+="操作符来进行委托合并

复制代码 代码如下:

d4 += d1;

这时,由于d4初始值为null,在使用"+="操作(Combine方法)构造委托链时,将返回另外一个参数d1,再将d1的引用赋给d4(通过"ILSpy"查看,如下图)。也就是说,这时d4将指向d1所指向的对象。

3. 继续执行委托合并,并查看d4的变化

复制代码 代码如下:

d4 += d2;

这时可以看到调用列表的变化,_invocationList包含两个元素,分别是d1和d2.

4. 最后进行一次委托合并,把d3合并到d4中

复制代码 代码如下:

d4 += d3;

可以看到最新的d4实例中,调用列表已经包含了d3。

注意:由于委托是不可变的,所以这里应该描述为,d3和d4的Combine 产生了一个新的委托实例,新的委托实例的调用列表是d3和d4的合并;操作完成后,d4变量将指向新的委托实例的引用。

疑问:其实在这步调试过程中有个疑问,_invocationCount的值是3,但是_invocationList中有四个元素,最后一个为null,找了一下也没发现为什么,望高手看到帮忙解答。

所以对委托链建立的方法Delegate.Combine(Delegate A, Delegate B),可以进行下面的概括:

1.如果A和B均为null,则返回null。
2.如果A或B一个为null而另一个不为null,则返回不为null的委托。
3.如果A和B均不为null,返回一个新的委托(委托是不可变的),该委托_invocationList字段为一个委托数组,该数组中委托的顺序为:A中_invacationList所指向的委托数组 + B中_invacationList所指向的委托数组。

移除委托链

我们可以通过Delegate类的静态方法Remove,从一个委托链中移除一个委托,这里就不做演示了。

注意:当调用Remove时,会遍历(倒序)第一个参数中的中的调用列表(_invocationList), 找到与第二个参数的_target和_methodPtr字段相匹配的委托,并将其从委托列表中移除。

当有多个匹配的情况是,Remove方法只移除第一个匹配的委托;但是,可以通过RemoeAll方法来移除所有匹配的委托。

同样对委托移除的方法Delegate.Remove(Delegate A, Delegate B),可以进行下面的概括:

1.如果A为null,返回null。
2.如果B为null,返回A。
3.如果A的_invocationList为null,即不包含委托链,那么如果A本身与B匹配,则返回null,否则返回A。
4.如果A的_invocationList中不包含与B匹配的委托,则返回A。
5.如果A的_invocationList中包含与B匹配的委托,则从链表中移除B,然后
6.如果A的链表中只剩下一个委托,则返回该委托。
6-1).如果A的链表中还剩下多个委托,将重新构建一个新的委托,并且新的委托的_invocationList为A的6-2)._invocationList移除了B之后的List。

总结

通过这篇文章,进一步认识了委托类型,然后通过一个例子观察了委托链的建立以及调用列表的变化。

通过这两篇文章,对委托应该有了一定的认识:

1.通过delegate关键字声明委托类型

复制代码 代码如下:

[<修饰符>] delegate <返回类型> <委托名> ([<形参表>])

2.找到与委托签名相符的方法来创建委托实例,也可以通过"+="和"-="来组合和移除委托
复制代码 代码如下:

new <委托类型名> (<方法>)

3.通过委托实例调用委托

相关文章

  • C# List<T>的用法小结

    C# List<T>的用法小结

    本篇文章主要是对C#中List<T>的用法进行了详细的总结介绍,需要的朋友可以过来参考下,希望对大家有所帮助
    2014-01-01
  • C#实现十字链表的使用示例

    C#实现十字链表的使用示例

    十字链表是一种将数据存储在节点中的数据结构,每个节点包含两个指针,分别指向下一个节点和上一个节点,通过定义节点类和链表类,实现十字链表的创建、遍历、插入和删除等操作,本文就来实现一下
    2023-11-11
  • c#如何实现接口事件

    c#如何实现接口事件

    这篇文章主要介绍了c#如何实现接口事件,帮助大家更好的理解和学习c#,感兴趣的朋友可以了解下
    2020-10-10
  • C#最小二乘法拟合曲线成直线的实例

    C#最小二乘法拟合曲线成直线的实例

    这篇文章主要介绍了C#最小二乘法拟合曲线成直线的实例,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-02-02
  • c# 复写Equals方法的实现

    c# 复写Equals方法的实现

    本文主要介绍了c# 复写Equals方法的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-05-05
  • C#调用Windows的API实现窗体动画

    C#调用Windows的API实现窗体动画

    在VF、VB、PB的应用中,有些无法通过语言工具本身来完成的或者做得不理想的功能,我们会考虑通过Windows的API来完成。本文就来通过调用Windows的API实现窗体动画,感兴趣的可以尝试一下
    2022-11-11
  • C#数据库操作类AccessHelper实例

    C#数据库操作类AccessHelper实例

    这篇文章主要介绍了C#数据库操作类AccessHelper实例,可实现针对access数据库的各种常见操作,非常具有实用价值,需要的朋友可以参考下
    2014-10-10
  • c#中SqlHelper封装SqlDataReader的方法

    c#中SqlHelper封装SqlDataReader的方法

    这篇文章主要介绍了c#中SqlHelper封装SqlDataReader的方法,涉及C#针对数据库相关操作封装与使用的技巧,需要的朋友可以参考下
    2015-05-05
  • C# WPF后台动态添加控件实战教程

    C# WPF后台动态添加控件实战教程

    最近尝试用wpf在后台动态添加控件,所以下面这篇文章主要给大家介绍了关于C# WPF后台动态添加控件的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-05-05
  • C#中Try-Catch语句真的影响程序性能吗?

    C#中Try-Catch语句真的影响程序性能吗?

    这篇文章主要介绍了C#中Try-Catch语句真的影响程序性能吗?本文结合IL分析Try-Catch语句的性能问题,需要的朋友可以参考下
    2015-06-06

最新评论