C# 委托与事件

    在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, System.EventArgs.Empty)。
 
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值