[CLR via C#]委托

委托是一种新的面向对象语言特性,委托的功能是在CLR的支持下实现的,这就意味着它并不受限于特定的编程语言,比如C#使用delegate关键字来定义委托,其他的.NET编程语言可以使用自己的方式来定义委托。

以委托作为基础,.NET构造了一个技术大厦,事件驱动、异步调用和Lambda表达式都建立于委托之上,还有许多其他的技术与委托有着密切的联系,掌握委托是探索这些技术领域的前提。

一、委托的简单使用

namespace ConsoleApplication1
{
    public delegate int MathOptDelegate(int x, int y);

    class Program
    {
        static void Main(string[] args)
        {
            MathOptDelegate mathOpt = Add;

            int z = mathOpt(3, 2);
        }

        private static int Add(int x, int y)
        {
            return x + y;
        }
    }
}

从上例可以直观的感受到:委托可以看成是一个方法的"容器",将某一具体的方法"装入"后,就可以把它当成方法一样使用。但是不是所有的方法都可以赋值给mathOpt变量,必须满足一定的条件,定义委托类型时对方法的要求被称为方法的"签名"。

二、深入探索委托技术内幕

1.详解委托类型

用ILDASM打开编译后的项目文件,可以查看代码生成的IL指令。

不难发现使用delegate关键字定义一个委托类型时,其实是定义了一个新类MathOptDelegate,此类派生自MulticastDelegate,而MulticastDelegate又派生自Delegate。可以看到有一个构造函数和Invoke方法,继续查看Main方法,如下图所示:

查看IL_0007代码,发现调用了新生成的MathOptDelegate的构造函数,查看IL_0010代码,发现调用了MathOptDelegate的Invoke方法。所以通过委托变量间接调用方法,实际调用的是MathOptDelegate对象的Invoke方法。

通过查看IL代码,可以看到委托的真实面目,对于以下这条委托定义语句:

public delegate int MathOptDelegate(int x, int y);

C#编译器实际上是按照以下这个"代码模板"进行编译的:

public class MathOptDelegate : System.MultiDelegate
{
    public MathOPtDelegate(Object target,Int32 methodPtr);

    public Int32 virtual Invoke (Int32 value1,Int32 value2);

    public virtual IAsyncResult BeginInvoke(Int32 value1,Int32 value2, AsyncCallback callback,Object object);

    public virtual Int32 EndInvoke(IAsyncResult result);

}

注意MathOptDelegate类的构造函数,它接收两个参数target和methodPtr。

target:引用要调用方法的对象,如果调用的是静态方法,则target = null。

methodPtr:是一个方法指针,代表要调用的对象方法

2.委托调用列表

如果委托仅仅是方法调用的另一种方式,那何必多此一举引入"委托"这一特性?直接调用方法不更简单明了?对此问题的回答是:委托变量不仅可以引用一个方法,还可以组合多个方法并批量执行它们。

namespace ConsoleApplication1
{
    public delegate int MathOptDelegate(int x, int y);

    class Program
    {
        static void Main(string[] args)
        {
            MathOptDelegate a = Add;

            MathOptDelegate b = Divide;

            MathOptDelegate c = a + b;

            MathOptDelegate d = c - a;

            Console.ReadKey();
        }

        private static int Add(int x, int y)
        {
            return x + y;
        }

        private static int Divide(int x, int y)
        {
            return x / y;
        }
    }
}

Delegate定义了一个GetInvocationList静态方法用于获取委托调用列表(委托调用列表其实是委托对象内部所包容的一个数组,存放在数组中的元素是Delegate类型的实例,每个实例引用一个静态或实例方法),如果在代码中调用委托变量,将导致委托调用列表中的所有方法顺序执行,如果委托定义的方法有返回值,则多路委托变量的返回值为委托调用列表中最后一个方法的返回值。

但是委托创建之后,它的委托调用列表是不可更改的。使用"+"或者"-"运算符合并或分割两个委托调用列表,得到的其实是一个新的委托调用列表,可以参考字符串是不可更改的情况。

三、使用预定义的委托

.NET为了方便开发人员的使用,预先定义了几种委托,分别是Action、Function以及Predicate,使用起来都比较简单,所以就不再多介绍。

四、匿名方法和Lambda表达式

使用委托有几个步骤:

1)定义委托类型:

2)定义一个或多个符合委托类型要求的方法;

3)定义委托类型的变量;

4)将第2步定义的方法引用"挂接"到第3步定义的变量,以构建一个"委托调用列表";

5)通过委托变量"间接"调用委托调用列表;

显然,上述步骤很麻烦,能否简化?比如赋值给委托变量的方法只在这一个地方用到,其他的地方都不会调用它,为何需要单独定义成一个方法。基于上述简化开发的考虑,C#引入了"匿名方法"和"Lambda表达式"。

1.匿名方法揭秘

namespace ConsoleApplication1
{
    public delegate int MathOptDelegate(int x, int y);

    class Program
    {
        static void Main(string[] args)
        {
            MathOptDelegate a = delegate(int x, int y)
            {
                return x + y;
            };

            Console.WriteLine(a(2, 3));

            Console.ReadKey();
        }

    }
}

从示例中可以看到,"匿名方法"其实是将方法定义与委托变量赋值两个步骤合在一起,从而省掉了单独定义一个方法的麻烦。

使用ildasm查看上述代码生成的文件,会发现C#编译器自动生成了一个静态的方法:

2.奇特的Lambda表达式

上述示例中给委托变量赋值的语句

MathOptDelegate a = delegate(int x, int y){ return x + y; };

如果使用Lambda表达句,可以进一步简化: MathOptDelegate a  = (x,y) => {return x + y};

所以:Lambda表达式其实就是匿名方法的进一步简化,可以用于定义一个匿名函数,交将其传递给一个委托变量。

时间: 2024-10-25 13:55:44

[CLR via C#]委托的相关文章

读书笔记—CLR via C#委托和attribute

前言 这本书这几年零零散散读过两三遍了,作为经典书籍,应该重复读反复读,既然我现在开始写博了,我也准备把以前觉得经典的好书重读细读一遍,并且将笔记整理到博客中,好记性不如烂笔头,同时也在写的过程中也可以加深自己理解的深度,当然同时也和技术社区的朋友们共享 委托 类型安全的回调函数,函数签名定义声明.指向静态或实例方法 派生自System.MulticastDelegate的类 将方法绑定到委托时,C#和CLR都允许引用类型的协变性和逆变性 协变性指方法能返回从委托的返回类型派生的类型 逆变性是指

CLR之委托的揭秘(一)

初识委托:          在之前的学习中我们已经可以把对象,值,数组当作参数传递给方法,但是有没有可能把方法也当作参数传递给方法呢?有了这个想法于是就有了委托.方法当作一种参数去传递,但是方法有的有返回值有的没有返回值,这如何处理?委托又用在什么地方?通过这篇文章我们来学习一下委托的用法 委托解密:              在C#中要使用委托.必选先定义要使用的委托,在使用时需要创建该委托的一个或多个实例,以下示例展示了如何声明委托             1 /* 2 委托的安全性非常高

clr 调用c# 混合编程

msdn上的关于如何在clr中设置委托的学习资料. https://technet.microsoft.com/zh-CN/library/c320cx3h.aspx/ msdn中关于如何在MFC中调用C#界面的资料 https://msdn.microsoft.com/zh-cn/library/b1kyh79x(v=vs.120).aspx

泛型接口、泛型委托、泛型方法、泛型约束

泛型接口 没有泛型接口,每次试图使用一个非泛型接口(如IComparable)来操纵一个值类型时,都会进行装箱,而且会丢失编译时的类型安全性.这会严重限制泛型类型的应用.所以,CLR提供了对泛型接口的支持.一个引用类型或值类型为了实现一个泛型接口,可以具体指定类型实参:另外,一个类型也可以保持类型实参的未指定状态来实现一个泛型接口.来看一些例子: 以下泛型接口定义是作为FCL的一部分发布的: public interface IEnumerable<T> : IDisposable, IEnu

《CLR.via.C#第三版》第二部分第12章节 泛型 读书笔记(六)

终于讲到泛型了.当初看到这个书名,最想看的就是作者对泛型,委托,反射这些概念的理解.很多人对泛型的理解停留在泛型集合上,刚开始我也是,随着项目越做越多,对待泛型的认识也越来越深刻. 泛型的概念:泛型是一种特殊的类型,它把指定类型的工作推迟到客户端代码声明并实例化类或方法的时候进行. 泛型的优势:源代码保护.类型安全.更加清晰的代码.更佳的性能. 原理:(关键字:开放类型,封闭类型)所有带泛型参数的类型都是一个开放式类型,它不能被实例化(类似接口),在具体使用时生成封闭类型(实际数据类型). 泛型

CLR via C#关于泛型(Generics )的摘录

泛型,是CLR和编程语言提供的一种特殊机制,它支持另一种形式的代码重用,即“算法重用”. 简单的说,开发人员先定义好一个算法,比如排序.搜索.交换.比较或者转换等.但是,定义算法的开发人员并不设改算法要操作什么数据类型:改算法可广发地应用于不同类型的对象.然后,另一个开发人员只要指定了算法要操作的具体数据类型,就可以开始使用这个现成的算法了.例如,可用一个排序算法来操作Int32 和String等类型的对象. 1.大多数算法都封装在一个类型中,CLR允许创建泛型引用类型和泛型值类型,但不允许创建

C#泛型方法解析

C#2.0引入了泛型这个特性,由于泛型的引入,在一定程度上极大的增强了C#的生命力,可以完成C#1.0时需要编写复杂代码才可以完成的一些功能.但是作为开发者,对于泛型可谓是又爱又恨,爱的是其强大的功能,以及该特性带来的效率的提升,恨的是泛型在复杂的时候,会呈现相当复杂的语法结构.这种复杂不仅是对于初学者,对于一些有开发经验的.NET开发者,也是一个不那么容易掌握的特性. 接下来我们来了解一下C#2.0加入的特性:泛型. 一.泛型的基本特性概述: 在实际项目开发中,任何API只要将object作为

CLR环境中内置了几个常用委托(转)

CLR环境中给我们内置了几个常用委托Action. Action<T>.Func<T>.Predicate<T>,一般我们要用到委托的时候,尽量不要自己再定义一 个委托了,就用系统内置的这几个已经能够满足大部分的需求,且让代码符合规范.一.Action Action封装的方法没有参数也没有返回值,声明原型为: 1 public delegate void Action(); 用法如下: public void Alert() { Console.WriteLine(&q

CLR类型设计之方法与构造器

C#语言中最常用到的就是方法的语法,如果在控制台应用程序中,一定要有一个Main()方法作为程序入口,本文讨论的不是方法实现也不是为什么要写方法,而是来讨论下如何构造器和扩展方法. 无论学习那门语言都要学习函数体,C#,JAVA,PHP,都会涉及到函数体,而C#的函数体成员并不少,方法和构造器就是函数体成员之一,函数体成员还包括但不限于:方法,属性,构造器,终结器,运算符及索引器. 方法就是某个类相关的函数,也可以返回简单的基元类型或者什么也不反回,方法可以定义其公开性,如果使用static修饰