委托幕后花絮






4.88/5 (21投票s)
描述委托的完整类和异步处理
目标读者
本文面向对 C# 中的委托有基本了解的人。
引言
我们都知道 .NET 世界中的“委托”一词。但是当编译器在您的程序中看到“委托”一词时会做什么呢?编译器和 CLR 会执行大量的幕后处理来隐藏复杂性。我将重点介绍编译器和 CLR 如何协同工作来实现委托。本文将向您介绍委托的完整类以及委托执行的异步处理。
Using the Code
public delegate void MyDelegate(int intValue);
编译器实际上定义了一个完整的类,如下所示
class MyDelegate : System.MulticastDelegate
{
//constructor
public MyDelegate(Object object, Inptr method);
//same prototype as specified in the source code
public virtual void Invoke(Int32 intValue);
//delegate methods can be called asynchronously
public virtual IAsyncResult BeginInvoke(Int32 intValue,
AsyncCallback callback, object object);
public virtual void EndInvoke(IAsyncResult result);
}
ILdasm.exe 显示编译器为委托生成的元数据。
编译器定义的类有四个方法
- 构造函数
Invoke
方法BeginInvoke
方法EndInvoke
方法
Invoke
方法用于在同一线程中调用目标方法。这是一个同步过程。我将在本文后面向您介绍 BeginInvoke
和 EndInvoke
方法。在此之前,我们想了解方法(静态和实例)如何在委托内部被指向,这可能是委托中最显着的功能。有三个非公共字段:
_target
当委托对象包装一个static
方法时,此字段为null
。当委托对象包装实例方法时,此字段引用该对象。_methodptr
它用于标识要回调的方法。_invocationlist
此字段通常为null
,但在构建委托链时,它可以引用委托数组。
MyDelegate objStatic = new MyDelegate(Class.StaticMethod);
MyDelegate objInstance = new MyDelegate(new Class().InstanceMethod);
_target
字段保存 System.Object
类型。对对象的引用将传递给构造函数的 object 参数,并且标识该方法的特殊 intptr
值将传递给 method 参数。对于 static
方法,null
将传递给 object 参数。在构造函数内部,这两个参数分别保存在 _target
和 _methodptr private
字段中。
在内部,combine
方法看到 objChain
已经引用了一个委托对象,因此 combine
将构造一个新的委托对象。这个新的委托对象将其 private _target
和 _methodptr
字段初始化为超出此处范围的值。重要的是,_invocationlist
字段被初始化为引用委托对象数组。数组的第一个元素(索引 0)将被初始化为引用包装 static
方法的委托。数组的第二个元素(索引 1)将被初始化为引用包装 Instance 方法的委托。
MyDelegate objStatic1 = new MyDelegate(Class.StaticMethod1);
MyDelegate objInstance2 = new MyDelegate(new Class().InstanceMethod2);
MyDelegate objChain = null;
objChain = (MyDelegate) Delegate.Combine( objChain, objStatic1);
objChain = (MyDelegate) Delegate.Combine( objChain, objInstance2);
异步委托
委托可以以异步方式调用方法。如果调用了 BeginInvoke
方法,则 CLR 将对请求进行排队并立即返回到主线程。目标方法将在线程池中的一个线程上调用。主线程可以与目标方法并行执行。BeginInvoke
方法有三个参数
- 输入参数
AsyncCallBack
委托实例- 异步状态信息
objDel.BeginInvoke(5,new AsyncCallback(MyCallback),"Result From Main Program");
最后两个参数用于提供一个回调机制,该机制将在目标方法完成时被调用。这称为 AsyncCallBack
委托。
delegate void AsyncCallback( IAsyncResult ar );
如果在 BeginInvoke
中未指定回调,则在原始线程中使用 EndInvoke
。它用于获取异步处理的返回值。如果存在回调,则将 EndInvoke
放置在回调方法中。
以下是异步委托的示例程序
public delegate int MyDelegate(int intX);
class Program
{
static void Main(string[] args)
{
MyClass objClass = new MyClass();
MyDelegate objDel = new MyDelegate(objClass.MyMethod);
//Asynchronous processing begins here
IAsyncResult AsyncRes = objDel.BeginInvoke
(5, new AsyncCallback(MyCallBack), "State info from main thread");
Console.WriteLine("From main thread ");
Console.ReadLine();
}
public static void MyCallBack(IAsyncResult ar)
{
AsyncResult Result = (AsyncResult)ar;
MyDelegate objDel = (MyDelegate)Result.AsyncDelegate;
int intResult = objDel.EndInvoke(ar);
Console.WriteLine("Output from callback: " + intResult);
//state information from the main thread
string strMessage = (string)ar.AsyncState;
Console.WriteLine(strMessage);
}
}
class MyClass
{
public int MyMethod(int intX)
{
Thread.Sleep(10000);
return (intX * intX);
}
}
输出

结论
委托是一个引用方法的对象。它可以通过在另一个线程中运行方法用于异步或同步进程。异步进程主要用于后台处理,如邮件、数据库事务等,因此用户可能不必等到操作完成。
历史
- 2009 年 3 月 23 日:初始帖子