65.9K
CodeProject 正在变化。 阅读更多。
Home

委托幕后花絮

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.88/5 (21投票s)

2009 年 3 月 23 日

CPOL

3分钟阅读

viewsIcon

36862

描述委托的完整类和异步处理

目标读者

本文面向对 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 方法用于在同一线程中调用目标方法。这是一个同步过程。我将在本文后面向您介绍 BeginInvokeEndInvoke 方法。在此之前,我们想了解方法(静态和实例)如何在委托内部被指向,这可能是委托中最显着的功能。有三个非公共字段:

  1. _target
    当委托对象包装一个 static 方法时,此字段为 null。当委托对象包装实例方法时,此字段引用该对象。
  2. _methodptr
    它用于标识要回调的方法。
  3. _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 方法有三个参数

  1. 输入参数
  2. AsyncCallBack 委托实例
  3. 异步状态信息
    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 日:初始帖子
© . All rights reserved.