谈及到C#的基本特性,“委托”是不得不去了解和深入分析的一个特性。对于大多数刚入门的程序员谈到“委托”时,都会想到“将方法作为方法的参数进行传递”,很多时候都只是知道简单的定义,主要是因为“委托”在理解上有较其他特性比较难的地方。在本次说明中,不会将委托的简单声明和调用作为重点。

“委托”不需要直接定义一个要执行的行为,而是将这个行为用某种方法“包含”在一个对象中。这个对象可以像其他任何对象那样使用。在该对象中,可以执行封装的操作。可以选择将委托看作之定义了一个方法的接口,将委托的实例看作实现了那个接口的对象。

在“委托”的相关定义中,我们可以不难看出,“委托与方法“相比较于“接口与类”有着设计理念上的相似部分,产生的背景源于”设计原则“中的”开放-封闭原则“,”开放-封闭“原则:是说软件实体(类,模块,函数等等)应该可以扩展,但是不可修改。换一种说法可能更好的理解”对于扩展是开放的,对于更改是封闭的“,面对新的需求,对于程序的改动是通过增加新的代码进行的,而不是更改现有的代码。

在C#中委托用delegate关键字定义,使用new操作符构造委托实例,采用传统的方法调用语法来回调函数(只是要用引用了委托对象的一个变量代替方法名)。在C#中,委托在编译的时候会被编译成类。对于委托的一个说明:委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递。委托类既可嵌套在一个类型中定义,也可以在全局范围内定义。由于委托是类,凡是可以定义类的地方,都可以定义委托。

接下来我们来看一下”委托“的组成,需要满足的条件:

1.声明委托类型。

2.必须有一个方法包含了要执行的代码。

3.必须创建一个委托实例。

4.必须调用委托实例。

接下来大致的了解一下上面所提出的4项条件:

委托类型实际上只是参数类型的一个列表以及返回类型。规定了类型的实例能表示的操作。在调用一个委托实例的时候,必须保证使用的参数完全匹配,而且能以指定的方式使用返回值。对于委托实例的创建,取决于操作使用实例方法还是静态方法(如果操作是静态方法,指定类型名称就可以,如果是操作实例方法,需要先创建类型的实例)。对于委托的调用,可以直接调用委托的实例的方法就可以完成对应的操作。

以上谈及了”委托“的定义和组成,接下来我们来了解一下如何将方法绑定到”委托“上,以及委托的合并和删除。

可以将多个方法赋给同一个委托,委托实例实际有一个操作列表与之关联。在System.Delegate类型中提供了两个静态方法Combine()和Remove()负责委托实例的新增和删除操作。但是在我们的实际开发中,较多的采用-=和+=操作符。

在FCL中,所有的委托类型都派生自MulticastDelegate,该类型在System.MulticastDelegate类型中。

具体来看一下Combine()方法的底层实现代码:

 [System.Runtime.InteropServices.ComVisible(true)]
public static Delegate Combine(params Delegate[] delegates)
{
if (delegates == null || delegates.Length == )
return null; Delegate d = delegates[];
for (int i = ; i < delegates.Length; i++)
d = Combine(d,delegates[i]); return d;
}
public static Delegate Combine(Delegate a, Delegate b)
{
if ((Object)a == null)
return b; return a.CombineImpl(b);
}

以上两个方法为System.Delegate类型中,CombineImpl方法在MulticastDelegate重写。

        [System.Security.SecuritySafeCritical]
protected override sealed Delegate CombineImpl(Delegate follow)
{
if ((Object)follow == null)
return this;
if (!InternalEqualTypes(this, follow))
throw new ArgumentException(Environment.GetResourceString("Arg_DlgtTypeMis")); MulticastDelegate dFollow = (MulticastDelegate)follow;
Object[] resultList;
int followCount = ;
Object[] followList = dFollow._invocationList as Object[];
if (followList != null)
followCount = (int)dFollow._invocationCount; int resultCount;
Object[] invocationList = _invocationList as Object[];
if (invocationList == null)
{
resultCount = + followCount;
resultList = new Object[resultCount];
resultList[] = this;
if (followList == null)
{
resultList[] = dFollow;
}
else
{
for (int i = ; i < followCount; i++)
resultList[ + i] = followList[i];
}
return NewMulticastDelegate(resultList, resultCount);
}
else
{
int invocationCount = (int)_invocationCount;
resultCount = invocationCount + followCount;
resultList = null;
if (resultCount <= invocationList.Length)
{
resultList = invocationList;
if (followList == null)
{
if (!TrySetSlot(resultList, invocationCount, dFollow))
resultList = null;
}
else
{
for (int i = ; i < followCount; i++)
{
if (!TrySetSlot(resultList, invocationCount + i, followList[i]))
{
resultList = null;
break;
}
}
}
}
if (resultList == null)
{
int allocCount = invocationList.Length;
while (allocCount < resultCount)
allocCount *= ; resultList = new Object[allocCount]; for (int i = ; i < invocationCount; i++)
resultList[i] = invocationList[i]; if (followList == null)
{
resultList[invocationCount] = dFollow;
}
else
{
for (int i = ; i < followCount; i++)
resultList[invocationCount + i] = followList[i];
}
}
return NewMulticastDelegate(resultList, resultCount, true);
}
}

再来具体看一下Remove()方法的底层实现代码,RemoveAll和Remove两个方法为System.Delegate类型中,CombineImpl方法在MulticastDelegate重写。:

 public static Delegate RemoveAll(Delegate source, Delegate value)
{
Delegate newDelegate = null; do
{
newDelegate = source;
source = Remove(source, value);
}
while (newDelegate != source); return newDelegate;
}
[System.Security.SecuritySafeCritical]
public static Delegate Remove(Delegate source, Delegate value)
{
if (source == null)
return null; if (value == null)
return source; if (!InternalEqualTypes(source, value))
throw new ArgumentException(Environment.GetResourceString("Arg_DlgtTypeMis")); return source.RemoveImpl(value);
}
        [System.Security.SecuritySafeCritical]
protected override sealed Delegate RemoveImpl(Delegate value)
{ MulticastDelegate v = value as MulticastDelegate; if (v == null)
return this;
if (v._invocationList as Object[] == null)
{
Object[] invocationList = _invocationList as Object[];
if (invocationList == null)
{
if (this.Equals(value))
return null;
}
else
{
int invocationCount = (int)_invocationCount;
for (int i = invocationCount; --i >= ; )
{
if (value.Equals(invocationList[i]))
{
if (invocationCount == )
{
return (Delegate)invocationList[-i];
}
else
{
Object[] list = DeleteFromInvocationList(invocationList, invocationCount, i, );
return NewMulticastDelegate(list, invocationCount-, true);
}
}
}
}
}
else
{
Object[] invocationList = _invocationList as Object[];
if (invocationList != null) {
int invocationCount = (int)_invocationCount;
int vInvocationCount = (int)v._invocationCount;
for (int i = invocationCount - vInvocationCount; i >= ; i--)
{
if (EqualInvocationLists(invocationList, v._invocationList as Object[], i, vInvocationCount))
{
if (invocationCount - vInvocationCount == )
{
return null;
}
else if (invocationCount - vInvocationCount == )
{
return (Delegate)invocationList[i != ? : invocationCount-];
}
else
{
Object[] list = DeleteFromInvocationList(invocationList, invocationCount, i, vInvocationCount);
return NewMulticastDelegate(list, invocationCount - vInvocationCount, true);
}
}
}
}
} return this;
}

在以上的代码中,我们了解到了在.NET底层是如何实现委托实例的绑定和删除绑定。

在调用委托实例时,所有的操作都是顺序执行的。如果调用具有一个非void的返回类型,则调用的返回值是最后一个操作的返回值。如果调用列表中任何操作抛出异常,都会阻止执行后续的操作。

在上面提到了委托列表中出现非void实例调用,如果委托实例中出现多个非void调用,并且需要获取所有的委托实例的返回值结果,那么应该如何操作,在.NET红提供了一个方法GetInvocationList(),用于获取委托链表。

接下来具体了解一下GetInvocationList()的底层代码:

      [System.Security.SecuritySafeCritical]
public override sealed Delegate[] GetInvocationList()
{
Delegate[] del;
Object[] invocationList = _invocationList as Object[];
if (invocationList == null)
{
del = new Delegate[];
del[] = this;
}
else
{
int invocationCount = (int)_invocationCount;
del = new Delegate[invocationCount]; for (int i = ; i < invocationCount; i++)
del[i] = (Delegate)invocationList[i];
}
return del;
}

当获取到委托实例列表后,可采用循环迭代的方式,依次获取每个委托实例的返回值。

再来了解一个属性Method,具体看一下此属性的底层实现代码:

       public MethodInfo Method
{
get
{
return GetMethodImpl();
}
} [System.Security.SecuritySafeCritical]
protected virtual MethodInfo GetMethodImpl()
{
if ((_methodBase == null) || !(_methodBase is MethodInfo))
{
IRuntimeMethodInfo method = FindMethodHandle();
RuntimeType declaringType = RuntimeMethodHandle.GetDeclaringType(method);
if (RuntimeTypeHandle.IsGenericTypeDefinition(declaringType) || RuntimeTypeHandle.HasInstantiation(declaringType))
{
bool isStatic = (RuntimeMethodHandle.GetAttributes(method) & MethodAttributes.Static) != (MethodAttributes);
if (!isStatic)
{
if (_methodPtrAux == (IntPtr))
{
Type currentType = _target.GetType();
Type targetType = declaringType.GetGenericTypeDefinition();
while (currentType != null)
{
if (currentType.IsGenericType &&
currentType.GetGenericTypeDefinition() == targetType)
{
declaringType = currentType as RuntimeType;
break;
}
currentType = currentType.BaseType;
} BCLDebug.Assert(currentType != null || _target.GetType().IsCOMObject, "The class hierarchy should declare the method");
}
else
{
MethodInfo invoke = this.GetType().GetMethod("Invoke");
declaringType = (RuntimeType)invoke.GetParameters()[].ParameterType;
}
}
}
_methodBase = (MethodInfo)RuntimeType.GetMethodBase(declaringType, method);
}
return (MethodInfo)_methodBase;
}

以上是System.Delegate类中的定义,接下来看一下MulticastDelegate重写:

 [System.Security.SecuritySafeCritical]
protected override MethodInfo GetMethodImpl()
{
if (_invocationCount != (IntPtr) && _invocationList != null)
{
Object[] invocationList = _invocationList as Object[];
if (invocationList != null)
{
int index = (int)_invocationCount - ;
return ((Delegate)invocationList[index]).Method;
}
MulticastDelegate innerDelegate = _invocationList as MulticastDelegate;
if (innerDelegate != null)
{
return innerDelegate.GetMethodImpl();
}
}
else if (IsUnmanagedFunctionPtr())
{
if ((_methodBase == null) || !(_methodBase is MethodInfo))
{
IRuntimeMethodInfo method = FindMethodHandle();
RuntimeType declaringType = RuntimeMethodHandle.GetDeclaringType(method);
if (RuntimeTypeHandle.IsGenericTypeDefinition(declaringType) || RuntimeTypeHandle.HasInstantiation(declaringType))
{
RuntimeType reflectedType = GetType() as RuntimeType;
declaringType = reflectedType;
}
_methodBase = (MethodInfo)RuntimeType.GetMethodBase(declaringType, method);
}
return (MethodInfo)_methodBase;
}
return base.GetMethodImpl();
}

以上是对委托的相关定义,以及有关委托的一些操作方法的说明,没有具体指出如何去创建和使用委托,因为委托的简单创建和一般应用,对于大部分开发者来说是相对较为简单的,因为微软在不断的对C#的语法进行提升和修改,极大的简化了对应的操作。但是正是由于在应用层做了较大的封装,这也会导致特性在底层的复杂度慢慢的增大。

C#中的委托解析的更多相关文章

  1. .Net: C#中的委托(Delegate)和事件(Event)

    委托和事件在 .Net Framework中的应用非常广泛,然而,较好地理解委托和事件对很多接触C#时间不长的人来说并不容易.它们就像是一道槛儿,过了这个槛的人,觉得真 是太容易了,而没有过去的人每次 ...

  2. C# 中的委托和事件

    觉得这篇文章写的非常好,大神之作,由简入繁,对我这种初学者来说帮忙很大,特此留存下. 摘自:http://tracefact.net/CSharp-Programming/Delegates-and- ...

  3. Objective-C中的委托(代理)模式

    我个人更喜欢把委托(Delegate)模式称为代理(Proxy)模式.还是那句话,第一次接触代理模式是在Java中接触的,在Java中实现代理模式和接口是少不了的.当时学习Spring的时候用到了接口 ...

  4. C# 中的委托和事件(转)

    引言 委托 和 事件在 .Net Framework中的应用非常广泛,然而,较好地理解委托和事件对很多接触C#时间不长的人来说并不容易.它们就像是一道槛儿,过了这个槛的人,觉得真是太容易了,而没有过去 ...

  5. C# 中的委托和事件(转载)

    引言 委托 和 事件在 .Net Framework中的应用非常广泛,然而,较好地理解委托和事件对很多接触C#时间不长的人来说并不容易.它们就像是一道槛儿,过了这个槛的人,觉得真是太容易了,而没有过去 ...

  6. 【转】C# 中的委托和事件

    阅读目录 C# 中的委托和事件 引言 将方法作为方法的参数 将方法绑定到委托 事件的由来 事件和委托的编译代码 委托.事件与Observer设计模式 .Net Framework中的委托与事件 总结 ...

  7. 2016 - 1- 23 iOS中xml解析 (!!!!!!!有坑要解决!!!!!!)

    一: iOS中xml解析的几种方式简介 1.官方原生 NSXMLParser :SAX方式解析,使用起来比较简单 2.第三方框架 libxml2 :纯C 同时支持DOM与SAX GDataXML: D ...

  8. 第3章 C#中的委托和事件

    .NET框架中的委托和事件 using System; using System.Collections.Generic; using System.Linq; using System.Text; ...

  9. 分分钟用上C#中的委托和事件之窗体篇

    上次以鸿门宴的例子写了一篇名为<分分钟用上C#中的委托和事件>的博文,旨在帮助C#初学者迈过委托和事件这道坎,能够用最快的速度掌握如何使用它们.如果觉得意犹未尽,或者仍然不知如何在实际应用 ...

随机推荐

  1. 隐私泄露杀手锏 —— Flash 权限反射

    [简版:http://weibo.com/p/1001603881940380956046] 前言 一直以为该风险早已被重视,但最近无意中发现,仍有不少网站存在该缺陷,其中不乏一些常用的邮箱.社交网站 ...

  2. SuperMap iClient for JavaScript 新手入门

    地理信息系统(英语:Geographic Information System,缩写:GIS)是一门综合性学科,结合地理学与地图学,已经广泛的应用在不同的领域,是用于输入.存储.查询.分析和显示地理数 ...

  3. 初探Vue

    Vue.js(读音/vju:/,类似于view),是近来比较火的前端框架,但一直没有怎么具体了解.实现过,就知道个啥的MVVM啦,数据驱动啦,等这些关于Vue的虚概念. 由于最近,小生在公司中,负责开 ...

  4. .net 分布式架构之任务调度平台

    开源地址:http://git.oschina.net/chejiangyi/Dyd.BaseService.TaskManager .net 任务调度平台 用于.net dll,exe的任务的挂载, ...

  5. Visual Studio 2012远程调试中遇到的问题

    有的时候开发环境没问题的代码在生产环境中会某些开发环境无法重现的问题,或者需要对生产环境代码进行远程调试该怎么办? Vs已经提供给开发者远程调试的工具 下面简单讲讲该怎么用,前期准备:1.本地登录账户 ...

  6. CRL快速开发框架系列教程五(使用缓存)

    本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...

  7. 玩转spring boot——结合jQuery和AngularJs

    在上篇的基础上 准备工作: 修改pom.xml <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi=&q ...

  8. CSS入门常见的问题

    写在前面:本文简单介绍一下css的三大特性:层叠性.继承性.优先级.以及margin,padding,浮动,定位几个知识点.限于水平,不深入探讨,仅作为学习总结. 1,三特性 1)层叠性:同标签同权重 ...

  9. AEAI DP V3.6.0 升级说明,开源综合应用开发平台

    AEAI DP综合应用开发平台是一款扩展开发工具,专门用于开发MIS类的Java Web应用,本次发版的AEAI DP_v3.6.0版本为AEAI DP _v3.5.0版本的升级版本,该产品现已开源并 ...

  10. WINDOWS系统下MYSQL安装过程中的注意事项

    1.首先MySQL的安装方式有两种:一种是MSI安装方式,很简单就像安装Windows软件一样.另外一种就是ZIP安装方式.这种相对而言比较麻烦.新手推荐MSI安装方式. 安装方式有以下两种: MSI ...