现在的位置: 首页 > 综合 > 正文

C#委托与事件*

2013年10月01日 ⁄ 综合 ⁄ 共 4247字 ⁄ 字号 评论关闭

在C# 中,委托类似于C++中的回调机制,声明一个委托类型,可以分别采用实例方法和静态方法实例化了委托,所不同的是采用静态方法实例化的委托,它的Target属性为null;而用实例方法实例化的委托,它的Target属性为该实例。但是这里要注意,如果你用Console.WriterLine(delegateObj.Target)输出该实例,结果是该实例的类型,而不是该实例的名字。

   
    C#中,有关键字delegate,又有类Delegate我们可以用delegate来创建一个类Delegate的实例:
      Delegate d = Delegate.CreateDelegate(Type type, object target, string method),CreateDelegate()方法有很多重载版本,视具体情况而定。这里要指出的就是,关于delegate的属性Target的就是类Delegate的公共属性Target。基于这一点,委托也可以动态创建,但必须提供相应的参数:委托的类型,类实例,委托要表示的实例方法的名称。
 
     委托链,可以将几个(必需是同类型)委托给串接起来,当委托触发时,就会按照添加是的顺序依次调用各个委托回调函数;特别是,当委托回调函数有返回值,只有最后一个委托回调函数的返回值能被带回,之前的都被会被覆盖;如果要获取委托链中每一个回调函数的返回值,则可以使用delegate.GetInvocationList()方法(其中delegate是一个委托),它将返回一个Delegate数组,然后遍历该数组,就得到每一个回调函数的返回值,不过要注意,虽然它返回的是Delegate数组,但是在遍历的时候,遍历元素的类型必须与delegate的类型一致,而不是Delegate类型,实例代码如下:

delegate String GetStatus();

Delegate[] arrayOfDelegates = status.GetInvocationList();

foreach (GetStatus getStatus in arrayOfDelegates)

 {

      try

      {

           report.AppendFormat("{0}{1}{1}",

           getStatus(), Environment.NewLine);

      }

      catch (Exception e)

      {

           Object component = getStatus.Target;

           report.AppendFormat( "Failed to get status from {1}{2}{0}   Error: {3}{0}{0}",Environment.NewLine,

           ((component == null) ? "" : component.GetType() + "."),getStatus.Method.Name, e.Message);

       }

}

其中status的类型是GetStatus,而GetStatus就是一个委托类型。注意上述代码中getStatus 的类型是GetStatus,而不是Delegate类型。同时,如果委托类型的返回值为Class(父类)存在一个返回值为Subclass(子类)的方法,那么这个方法也是可以用来初始化这个委托类型的,代码示例:

class Mammals
{
    public Mammals()
    {
        Console.WriteLine("Mammals");
    }
}

class Dogs : Mammals
{
    public Dogs()
    {
        Console.WriteLine("Dogs");
    }
}

class Program
{
    // Define the delegate.
    public delegate Mammals HandlerMethod();

    public static Mammals FirstHandler()
    {
        return new Mammals();
    }

    public static Dogs SecondHandler()
    {
        return new Dogs();
    }

    static void Main()
    {
        HandlerMethod handler1 = FirstHandler;

        handler1();
        Console.WriteLine();

        // Covariance allows this delegate.
        HandlerMethod handler2 = SecondHandler;
        handler2();
        Console.ReadLine();
    }
}

输出结果为

Mammals

 

Mammals

Dogs

 
        另外,如果委托类型的参数为Subclass(子类)存在一个参数为Class(父类)的方法,那么这个方法也是可以用来初始化一个委托类型的,代码示例如下:

class Mammals
{
    public Mammals()
    {
        Console.WriteLine("Mammals");
    }
}

class Dogs : Mammals
{
    public Dogs()
    {
        Console.WriteLine("Dogs");
    }
}

class Program
{
    public delegate void HandlerMethod(Dogs sampleDog);

    public static void FirstHandler(Mammals elephant)
    {
    }

    public static void SecondHandler(Dogs sheepDog)
    {
    }

    static void Main(string[] args)
    {
        // Contravariance permits this delegate.
        HandlerMethod handler1 = FirstHandler;
        handler1(new Dogs());
        Console.WriteLine();
        HandlerMethod handler2 = SecondHandler;
        handler2(new Dogs());
        Console.ReadLine();
    }
}
输出结果为:

Mammals

Dogs

 

Mammals

Dogs

      另外一个问题是,如果在委托链中有一个回调函数抛出了异常,不会影响其他的回调函数的执行,就好像异常没有发生一样。
 

      目前我们所见到的委托都是通过一个命名方法来声明一个委托,在C#2.0以后就引入了匿名方法来声明一个委托,其创建方法很简单,就是把代码块作为委托的参数来声明即可,如下:其中d就是一个用匿名方法实例化的委托。

delegate void Del(int x);

Del d = delegate(int k) { /* ... */ };

      这种委托的好处就是通过避免写一个方法来减少代码量,其最常见的一个应用就是创建线程。示例如下:

      System.Threading.Thread t1 = new System.Threading.Thread

      (delegate()

            {

                System.Console.Write("Hello, ");

                System.Console.WriteLine("World!");

        });

     t1.Start();

     但是匿名方法又很多限制,比如在其代码块不可以使用goto, break, continue等从代码块中跳到代码块外面,反之也是不可以的。

      命名与匿名方法代码混合使用,代码示例:

delegate void Printer(string s);

class TestClass

{

    static void Main()

    {

        Printer p = delegate(string j)

        {

            System.Console.WriteLine(j);

        };

        p("The delegate using the anonymous method is called.");

        p = new Printer(TestClass.DoWork);

        p("The delegate using the named method is called.");

    }

    static void DoWork(string k)

    {

        System.Console.WriteLine(k);

    }

}

 

事件 事件用于类或结构体通知对象来处理一些事情,事件是对delegate的应用,而且声明一个事件的时候,必须提供一个delegate类型,如下形式:

public delegate void SampleEventDelegate(object sender, EventArgs e);

public class SampleEventSource

{

    public event SampleEventDelegate SampleEvent;

}

通过delegate,事件可以封装类型安全的方法,并在被触发的时候调用。声明了一个事件后,可以注册多个方法

到这个事件中,并且这些方法的参数类型、返回值,即signature必须一致。如在一个事件中同时注册里几个方

法,那么可以通过事件的GetInvocationList()方法来返回一个Delegate数组,通过遍历这个数组,就可以得到所有

回调函数(事件处理函数)的信息。事件的调用形式必须与其声明时的delegate的signature(签名)一致,如上

面声明的事件SampleEvent,在调用的时候必须如下形式调用SampleEvent(object,Systme.EventArgs.Empty)

抱歉!评论已关闭.