C#委托与事件*

来源:互联网 发布:汉文化 知乎 编辑:程序博客网 时间:2024/05/14 21:10
在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)

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 小腿长疙瘩很痒怎么办 腿过敏起红疙瘩怎么办 肚子上起红疙瘩很痒怎么办 小蚂蚁咬了肿了怎么办 锦鲤鱼尾巴烂了怎么办 泰迪身上长白毛怎么办 鱼身上有红斑了怎么办 新买锦鲤不吃食怎么办 鱼身上有红血丝怎么办 大腿内侧有红色条纹怎么办 腿上出现红血丝怎么办 孕妇有脚气,很痒怎么办 孕晚期脚气很痒怎么办 孕期有脚气很痒怎么办 激光后留下色沉怎么办 腋下很黑怎么办怎样才能变白 屁股上长一块癣怎么办 鼻两侧一热发红怎么办 脸上起皮发红痒怎么办 自癜风发红发痒怎么办 脸又干又痒怎么办 脸发红还有点痛怎么办 脸过敏了红痒怎么办 脸两边一片红痒怎么办 婴儿大腿内侧破皮怎么办 宝宝大腿根淹了怎么办 大腿一走路就疼怎么办 下面痒怎么办用什么洗 长藓怎么办用什么药膏 小腿长湿疹很痒怎么办 产后排不出大便怎么办 3岁宝宝大便不通怎么办 腿上干燥像鱼鳞怎么办 一岁脸上长癣怎么办 脖子长了一片癣怎么办 深圳摇到车牌后怎么办 发现车被套牌了怎么办 我车牌被套牌了怎么办 车子被别人套牌怎么办 车被别人套牌了怎么办 被套牌了有违章怎么办