关键词:
谈及到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 == 0) return null; Delegate d = delegates[0]; for (int i = 1; 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 = 1; Object[] followList = dFollow._invocationList as Object[]; if (followList != null) followCount = (int)dFollow._invocationCount; int resultCount; Object[] invocationList = _invocationList as Object[]; if (invocationList == null) { resultCount = 1 + followCount; resultList = new Object[resultCount]; resultList[0] = this; if (followList == null) { resultList[1] = dFollow; } else { for (int i = 0; i < followCount; i++) resultList[1 + 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 = 0; i < followCount; i++) { if (!TrySetSlot(resultList, invocationCount + i, followList[i])) { resultList = null; break; } } } } if (resultList == null) { int allocCount = invocationList.Length; while (allocCount < resultCount) allocCount *= 2; resultList = new Object[allocCount]; for (int i = 0; i < invocationCount; i++) resultList[i] = invocationList[i]; if (followList == null) { resultList[invocationCount] = dFollow; } else { for (int i = 0; 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 >= 0; ) { if (value.Equals(invocationList[i])) { if (invocationCount == 2) { return (Delegate)invocationList[1-i]; } else { Object[] list = DeleteFromInvocationList(invocationList, invocationCount, i, 1); return NewMulticastDelegate(list, invocationCount-1, 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 >= 0; i--) { if (EqualInvocationLists(invocationList, v._invocationList as Object[], i, vInvocationCount)) { if (invocationCount - vInvocationCount == 0) { return null; } else if (invocationCount - vInvocationCount == 1) { return (Delegate)invocationList[i != 0 ? 0 : invocationCount-1]; } 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[1]; del[0] = this; } else { int invocationCount = (int)_invocationCount; del = new Delegate[invocationCount]; for (int i = 0; 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)0; if (!isStatic) { if (_methodPtrAux == (IntPtr)0) { 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()[0].ParameterType; } } } _methodBase = (MethodInfo)RuntimeType.GetMethodBase(declaringType, method); } return (MethodInfo)_methodBase; }
以上是System.Delegate类中的定义,接下来看一下MulticastDelegate重写:
[System.Security.SecuritySafeCritical] protected override MethodInfo GetMethodImpl() { if (_invocationCount != (IntPtr)0 && _invocationList != null) { Object[] invocationList = _invocationList as Object[]; if (invocationList != null) { int index = (int)_invocationCount - 1; 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#的语法进行提升和修改,极大的简化了对应的操作。但是正是由于在应用层做了较大的封装,这也会导致特性在底层的复杂度慢慢的增大。
本文出自 “彭泽0902” 博客,请务必保留此出处http://pengze0902.blog.51cto.com/7693836/1875423
c#中的委托(转)
C#中的委托和事件引言委托和事件在.NetFramework中的应用非常广泛,然而,较好地理解委托和事件对很多接触C#时间不长的人来说并不容易。它们就像是一道槛儿,过了这个槛的人,觉得真是太容易了,而没有过去的人每次见到委... 查看详情
c#中的委托
委托和事件在.NETFramework中的应用非常广泛,然而,较好地理解委托和事件对很多接触C#时间不长的人来说并不容易。它们就像是一道槛儿,过了这个槛的人,觉得真是太容易了,而没有过去的人每次见到委托和事件就觉... 查看详情
c#中的委托与事件
委托与事件是C#中的重要概念,这两个概念既有联系又有区别,容易混淆,下面就对C#中的这两个概念进行一下比较。1.委托声明委托的语法和声明函数非常类似,不过需要使用delegate关键字,并没有函数体。例... 查看详情
js中的回钓函数,c#中的委托
$(function(){myfunction(sayHi);}); varsayHi=function(){alter(‘你好‘);} functionmyfunction(a){a();} 查看详情
c#中的泛型委托(@whitetaken)
今天学习一下c#中的泛型委托。1.一般的委托,delegate,可以又传入参数(<=32),声明的方法为 publicdelegatevoidSomethingDelegate(inta);1usingSystem;2usingSystem.Collections.Generic;3usingSystem.Linq;4usingSystem.Text;5usingSystem.Th 查看详情
c#中的委托
...很是生疏。敲了几个例子自后,发现其实在面向对象语言中的委托和我们现实生活中是一样的。比如说,小红需要去银行取钱,这里取钱就是小红这一个对象的方法。同样如果是小明,他也可以有取钱这一个方法。如果小红由于... 查看详情
c#中的委托事件与接口(代码片段)
C#中的委托、事件与接口一、委托(一)委托链(二)匿名函数(三)Lamda语句二、事件三、接口一、委托委托:将方法以变量的形式传递,以方法的形式执行注意:赋值方法的返回类型、参数... 查看详情
c#中的委托和事件
C#中的委托和事件http://www.tracefact.net/CSharp-Programming/Delegates-and-Events-in-CSharp.aspx///<summary>///不好的设计///后期增加语种需要增加改动3个地方:语种枚举、switch-case、方法///</summary>publicclassBadDesign{publice 查看详情
c#中的委托是啥?事件是否一种委托?
委托委托类似于C/C++中的函数指针,它能够引用函数,但在C#中委托是一个对象,且是安全的。一个委托类型的变量可以引用一个或多个方法,这些方法由委托存放于一个调用列表中,当调用一个委托类型的变量即相当于依次调... 查看详情
C# 事件处理程序委托中的闭包? [复制]
】C#事件处理程序委托中的闭包?[复制]【英文标题】:ClosuresinC#eventhandlerdelegates?[duplicate]【发布时间】:2010-02-0903:13:12【问题描述】:目前我来自函数式编程背景,如果我不了解C#中的闭包,请见谅。我有以下代码来动态生成获... 查看详情
c#中的委托,个人理解(代码片段)
相当于Java中的interface定义的接口,但是 比Java中的interface要好 usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Text;usingSystem.Threading.Tasks;namespaceADONetDomepublicdelegatevoid 查看详情
快速理解c#中的委托与事件(代码片段)
以下内容只是个人理解,仅供参考。什么是委托?先看最简单的委托例子:namespaceDelegateTestpublicdelegatevoidMessageDelegate(stringname);classProgramprivatestaticvoidSaySomething(stringname)Console.WriteLine("Yousaid 查看详情
c#一次性删除委托中的所有方法
因为有的时候需要一次性删除委托中的所有方法,有时候不知道委托中挂载了哪些方法,即可通过委托的方法GetInvocationList得到此委托中所挂载的所有的方法一次行删除。来一个例子:using System;using System.Collections.Generic;us... 查看详情
分分钟用上c#中的委托和事件
每一个初学C#的程序猿,在刚刚碰到委托和事件的概念时,估计都是望而却步,茫然摸不到头脑的。百度一搜,关于概念介绍的文章大把大把的,当然也不乏深入浅出的好文章。可看完这些文章,大多数新手,估计也只是信心满... 查看详情
c#委托
...写得很好,所以转载过来。C#委托委托和事件在.NETFramework中的应用非常广泛,然而,较好地理解委托和事件对很多接触C#时间不长的人来说并不容易。引言C#委托和事件它们就像是一道槛儿,过了这个槛的人,觉得真是太容易了... 查看详情
分分钟用上c#中的委托和事件(代码片段)
每一个初学C#的程序猿,在刚刚碰到委托和事件的概念时,估计都是望而却步,茫然摸不到头脑的。百度一搜,关于概念介绍的文章大把大把的,当然也不乏深入浅出的好文章。可看完这些文章,大多数新手,估计也只是信心满... 查看详情
c#委托(delegate)
C#中的委托(Delegate)类似于C或C++中函数的指针。委托(Delegate)是存有对某个方法的引用的一种引用类型变量。引用可在运行时被改变。委托(Delegate)特别用于实现事件和回调方法。所有的委托(Delegate)都派生自System.Del... 查看详情
c#中的委托是啥?事件是否一种委托?
C#中的委托是什么?事件是不是一种委托?委托是CTS(公共类型系统)规定的5种类型之一(类类型、结构类型、接口类型、枚举类型、委托类型)。通俗的说,委托是这样一种类型:这种类型的实例,指向其它类型的方法。委托主... 查看详情