事件的解密

2019-12-14 09:00栏目:编程
TAG:

委托

原为出处:

在日前风流倜傥篇中写到了寄托,也说了委托是C#中众多特征的底子,那篇要讲的风浪,便是创建在委托之上的。在C#1.0中,委托和事件是最重视的四个特征。

委托与事件,经常拿来就用,往往忘记其落到实处原理,对其行使方式也尤其局限。周家安先生在《C# 6.0 学习笔记》中对信托和事件的讲课,深入浅出,清晰明了,故专程摘抄生龙活虎篇小说,勤看勤思。

在.NET在,大家平日利用委托,委托的成效不必多说,在.NET 2.0事情发生前,大家在运用委托在此以前,得自定义多个委托项目,再利用这么些自定义的寄托类型定义叁个寄托字段或变量。.NET 2.0给大家带给了Action、Func多少个泛型委托,.NET3.0给我们带给了拉姆da,那全体使得委托的定义和利用变得轻松起来。上边包车型大巴事例中的委托都选拔了Lambda表明式。

1、什么是事件?

首先,委托是意气风发种样式上与方式签名相似的类型。

风流倜傥.Action层层的泛型委托

事件设计到两类剧中人物——事件公布者和事件订阅者。当有个别事件产生后,事件宣布者会公布音讯;事件订阅者会吸收接纳到音信,并做出相应的拍卖,那就是事件的进程。

概念叁个寄托:

Action系列的寄托定义的是从未有过重回值(再次来到值为void卡塔尔(قطر‎的委托。它有多少个本子包含未有输入参数,1个输入参数,2个输入参数,3个输入参数,4个输入参数共5个本子那么些本子的原型如下:

 

public delegate void DoSome(string msg);

1.       没有输入参数再次回到值为void的委托.

2、使用事件

 

Action委托 封装二个艺术,该方法不行使参数并且不再次回到值。

2.1 定义事件

行使主要字 delegate, 类型名字为 DoSome(string msg卡塔尔.

能够利用此委托以参数形式传递三个试行某操作的主意,而不用显式声美赞臣个自定义的委托来封装此方法。该包裹的章程必须与此委托定义的章程签字相呼应。那代表该措施不得持有参数和重回值。例:

在C#中定义事件和定义类的积极分子是很经常的,只要一个event关键字就能够了。举个例子:

 

using System;

public event EventHandler birthday;

 

using System.Windows.Forms;

里面event是任重(Ren Zhong卡塔尔而道远字,而EventHandler是委托项目。

创建二个DoSome(string msg卡塔尔委托项目对象,并实例化。

public class Name

由此能够把事件定义的布局计算为:访谈修饰符 event 委托项目 事件名;个中央委员托项目能够是自定义的嘱托项目,也得以是.NET类库中预订义的嘱托项目伊芙ntHandler。

 1 static void TestDo(string str)
 2 {
 3      // ToDo
 4 }
 5 
 6 
 7 DoSome d1 = new DoSome(TestDo);
 8 
 9 
10 // 或者
11 
12 DoSome  d2;
13 
14 d2 = TestDo;

{

2.2 订阅和打消事件

 委托列表

   private string instanceName;

事件订阅者必要订阅事件揭橥者发表的事件信息,以便在事件被触发式选拔音讯并做出相应管理。在C#中,能够动用“+=”来订阅事件,使用“-=”来打消订阅事件。

 1 public delegate void DoSome(string msg);
 2 static void Main(string[] args)
 3 {
 4     DoSome d = new DoSome(TestDo);
 5     d("Hello");
 6     Console.WriteLine("--------------------------------------");
 7     d += new DoSome(Test1);
 8     d("123");
 9     Console.WriteLine("--------------------------------------");
10     d += TDo2;
11     d("world");
12     Console.WriteLine("--------------------------------------");
13     d -= TestDo;
14     d("nihao");
15 }
16 
17 static void TestDo(string str)
18 {
19     Console.WriteLine(str);
20 }
21 
22 static void Test1(string str)
23 {
24     Console.WriteLine("Test1 + " + str);
25 }
26 static void TDo2(string str)
27 {
28     Console.WriteLine("TDo2 + " + str);
29 }

   public Action ShowName;

public class Bridegroom
{
  //自定义委托
  public delegate void MarryHandler(string msg);
  //使用自定义委托类型定义事件,事件名称叫MarryEvent
  public event MarryHandler MarryEvent;

输出:

   public Show()

  //发出事件
  public void OnMarriageComing(string msg)
  {
    //判别是不是绑定了事件管理方法
    if(MarryEvent!=null)
    {
      //触发事件
      MarryEvent(msg);
    }
  }

图片 1

{

  static void Main(string[] msg)
  {
    Bridegroom bridegroom=new Bridegroom();

 

   If(ShowName != null)

    //实例化朋友对象
    Friend friend1=new Friend("张三");
    Friend friend2=new Friend("李四");
    Friend friend3=new Friend("王五");

事件

    ShowName();

    //使用“+=”来订阅事件
    bridegroom.MarryEvent+=new MarryHandler(friend1.SendMessage);
    bridgeroom.MarryEvent+=new MarryHandler(friend2.SendMessage);

 事件本身正是委托项目。

}

    //发出布告,这时独有订阅了事件的对象手艺接受通知
    bridegroom.OnMarriageComing("Friend,I Will Marry!!");
    Console.WriteLine("------------------------------------");

 1     class MyApp
 2     {
 3         public delegate void SpaceKeyPressedEventHandler();
 4 
 5         // 声明事件
 6         public event SpaceKeyPressedEventHandler SpaceKeyPressed;
 7 
 8         // 通过该方法引发事件
 9         protected virtual void OnSpaceKeyPressed()
10         {
11 
12             if (this.SpaceKeyPressed != null)
13             {
14                 // 将事件分发
15                 SpaceKeyPressed();
16             }
17         }
18 
19         // 启动事件监听的接口
20         public void StartRun()
21         {
22             // 监听事件
23             while (true)
24             {
25                 ConsoleKeyInfo keyinfo = Console.ReadKey();
26                 if (keyinfo.Key == ConsoleKey.Spacebar)
27                 {
28                     // 引发事件
29                     OnSpaceKeyPressed();
30                 }
31 
32                 if (keyinfo.Key == ConsoleKey.Escape)
33                 {
34                     // 跳出循环
35                     break;
36                 }
37             }
38         }
39     }
40 
41     class Program
42     {
43        
44         static void Main(string[] args)
45         {
46             MyApp app = new MyApp();
47             // 订阅事件,指定处理事件的方法
48             app.SpaceKeyPressed += app_SpaceKeyPressed;
49             app.SpaceKeyPressed += app_SecondEventHandler;
50 
51             // 启动事件监听
52             app.StartRun();
53         
54         }   
55 
56         // 事件处理1
57         private static void app_SpaceKeyPressed()
58         {
59             Console.WriteLine("{0} 按下空格键。", DateTime.Now.ToLongTimeString());
60         }
61         // 事件处理2
62         private static void app_SecondEventHandler()
63         {
64             Console.WriteLine("事件的第二个处理方法。");
65         }
66 
67     }

   public Name(string name)

    //使用"-="来撤消事件订阅,那时李四将收不到布告
    bridegroom.MarryEvent-=new MarryHandler(friend2.SendMessage);

平凡,作为事件委托,有两个参数,二个是Object类型,表示引发平地风波的对象,便是什么人抓住了事件的,超级多景况下在调用事件时是把类的眼下实例引用(this)传递过去。另一个参数是从System.EventArgs派省的类的实例。那是一个正式的事件管理程序的签定,为了标准事件的拍卖,.NET类库已经定义好八个System.伊夫ntHandler委托,用于注脚事件。它的原型如下:

   {

    bridegroom.MarryEvent+=new MarryHandler(friend3.SendMessage);

1 public delegate void EventHandler(object sender, EventArgs e);

      this.instanceName = name;

    bridegroom.OnMarriageComing("Friend,I Will Marry!!");

 引发平地风波的靶子实例将传递给sender参数,而与事件有关的多少则传递给e参数。如果无需传递过多的多寡,可以透过System.伊芙ntArgs.Empty静态成员再次回到一个空的EventArgs对象类传递。

   }

    Console.ReadKey();
  }
}

而是,由于分化的风云要传递的参数不一致,愈来愈多时候是从EventArgs类派生的子类的实例,鲜明二个EventHandler委托是不能知足各类意况的。要是针对分裂的风浪也顶三个三个对应的信托,水量生机勃勃旦多起来,既混乱,也倒霉管理。为了消除这一个标题,.NET类库又提供了叁个含有泛型参数的事件管理委托。原型如下:

   public void DisplayToConsole()

  public class Friend
  {
    public string Name;
    public Friend(string name)
    {
      Name=name;
    }
    //事件管理函数,该函数必要切合MarryHandler委托的定义
    public void SendMessage(string message)
    {
      Console.WriteLine(message);
      Console.WriteLine(this.Name+"收到了,届期候按期参预"卡塔尔国;
    }
  }

1 public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);

   {

值得注意的是,事件管理函数的概念供给予自定义的信托定义保持意气风发致,即参数个数,参数类型和重回类型等急需与寄托同风度翩翩。

T伊夫ntArgs 是三个泛型参数,应该是System.EventArgs类或然System.EventArgs类的派生类型。

      Console.WriteLine(this.instanceName);

除了那么些之外选拔自定义委托项目来定义事件外,还足以使用.NET类库中预约义的委托项目伊芙ntHandler来定义事件,需求在乎它们的参数。

泛型参数的风浪,实例:

   }

public class Bridegroom
{
  //使用.NET类库中的类型定义事件,事件名字为Marry伊芙nt
  public event EventHandler MarryEvent;

 1     // EventArgs 派生类
 2     // 创建泛型参数  KeyPressedEventArgs 类
 3     public class KeyPressedEventArgs : EventArgs
 4     {
 5         public ConsoleKey pressedKey { get; private set; }
 6         public KeyPressedEventArgs(ConsoleKey key)
 7         {
 8             pressedKey = key;
 9         }
10     }
11 
12     public class MyApp
13     {
14         // 捕捉按键的事件 声明一个泛型参数KeyPressedEventArgs类型的
15         public event EventHandler<KeyPressedEventArgs> KeyPressed;
16 
17         // 通过该方法引发事件
18         protected virtual void OnKeyPressed(KeyPressedEventArgs e)
19         {
20             if (this.KeyPressed != null )
21             {
22                 this.KeyPressed(this, e);
23             }
24         }
25 
26         // 事件监听端口启动
27         public void Start()
28         {
29             while (true)
30             {
31                 ConsoleKeyInfo keyInfo = Console.ReadKey();
32                 // 如果按下了ESC键,则退出循环
33                 if (keyInfo.Key == ConsoleKey.Escape)
34                 {
35                     break;
36                 }
37                 // 引发事件
38                 OnKeyPressed(new KeyPressedEventArgs(keyInfo.Key));
39             }
40         }
41     }
42 
43 
44     class Program
45     {
46        
47         static void Main(string[] args)
48         {
49             
50             MyApp app = new MyApp();
51             // 订阅事件,指定处理事件的方法
52             app.KeyPressed += app_KeyPressed;
53             // 启动事件监听
54             app.Start();
55         }   
56 
57         // 事件处理
58         private static void app_KeyPressed(Object sender, KeyPressedEventArgs e)
59         {
60             Console.WriteLine("已按下 {0} 键", e.pressedKey.ToString());
61         }
62         
63     }

   public void DisplayToWindow()

  //发出事件
  public void OnMarriageComing(string msg)
  {
    //判别是否绑定了事件管理方法
    if(MarryEvent!=null)
    {
      Console.WriteLine(msg);
      //触发事件
      MarryEvent(this,new EventArgs());
    }
  }

 

   {

  static void Main(string[] msg)
  {
    Bridegroom bridegroom=new Bridegroom();

      MessageBox.Show(this.instanceName);

    //实例化朋友对象
    Friend friend1=new Friend("张三");
    Friend friend2=new Friend("李四");
    Friend friend3=new Friend("王五");

   }

    //使用“+=”来订阅事件
    bridegroom.MarryEvent+=new MarryHandler(friend1.SendMessage);
    bridgeroom.MarryEvent+=new MarryHandler(friend2.SendMessage);

}

    //发出布告,那时唯有订阅了事件的对象技能接收布告
    bridegroom.OnMarriageComing("Friend,I Will Marry!!");
    Console.WriteLine("------------------------------------");

public class ActionStudy

    //使用"-="来撤销事件订阅,那个时候李四将收不到公告
    bridegroom.MarryEvent-=new MarryHandler(friend2.SendMessage);

{

    bridegroom.MarryEvent+=new MarryHandler(friend3.SendMessage);

   public static void Main()

    bridegroom.OnMarriageComing("Friend,I Will Marry!!");

   {

    Console.ReadKey();
  }
}

      Name testName = new Name("Koani");

public class Friend
{
  public string Name;
  public Friend(string name)
  {
    Name=name;
  }
  //事件处理函数,该函数要求契合MarryHandler委托的概念
  public void SendMessage(object s,EventArgs e)
  {
    Console.WriteLine(this.Name+"收到了,届时候定期参预"卡塔尔(قطر‎;
  }
}

      testName.ShowName  = () => testName.DisplayToWindow();

伊夫ntHandler是.NET类库中预约义的信托项目,用于拍卖不含有事件数量的事件。使用Reflector来查看伊芙ntHandler的现实性定义:

      testName.Show();

[Serializable, ComVisible(true), __DynamicallyInvokable]
public delegate void EventHandler(object sender, EventArgs e);

   }

从概念中得以观察,该委托类型的回来类型为void,第一个参数sender担当保存触发事件指标的援引,其品种为object;第一个参数e负担保存事件数量。伊芙ntArgs类也是.NET类库中定义的类,它不保留任何数据,要是想在事件中含有事件数量,就非得采纳伊芙ntArgs的派生类来完结。

}

2.3 扩展EventArgs类

2.       有1个输入参数再次来到值为void的寄托

上边说了,假若要在事变中蕴藏事件数量,就务须利用伊夫ntArgs的派生类。具体的实今世码如下:

Action<T>泛型委托封装叁个艺术,该方法只利用七个参数而且不再次回到值。

public class MarryEventArgs:EventArgs
{
  public string Message;
  public MarryEventArgs(string msg)
  {
    Message=msg;
  }
}

能够运用此委托以参数格局传递方式,而不用显式表明自定义的嘱托。该措施必需与此

public class Bridegroom
{
  //自定义委托项目,委托蕴含三个参数
  public delegate void MarryHandler(object sender,MarryEventArgs e);
  //使用自定义委托类型定义事件,事件名字为MarryEvent
  public event MarryHandler MarryEvent;
  //发出事件
  public void OnMarriageComing(string msg)
  {
    //判定是还是不是绑定了事件管理方法
    if(MarryEvent!=null)
    {
      //触发事件
      MarryEvent(this,new MarryEventArgs(msg));
    }
  }

信托定义的不二等秘书诀具名相呼应。也正是说,封装的不二等秘书诀必得持有五个由此值传递给它的参数,并且无法重临值。例:

  static void Main(string[] msg)
  {
    Bridegroom bridegroom=new Bridegroom();
    //实例化朋友对象
    Friend friend1=new Friend("张三");
    Friend friend2=new Friend("李四");
    Friend friend3=new Friend("王五");
    //使用“+=”来订阅事件
    bridegroom.MarryEvent+=new MarryHandler(friend1.SendMessage);
    bridgeroom.MarryEvent+=new MarryHandler(friend2.SendMessage);
    //发出文告,那时候唯有订阅了事件的对象工夫吸收接纳通告
    bridegroom.OnMarriageComing("Friend,I Will Marry!!");
    Console.WriteLine("------------------------------------");
    //使用"-="来打消事件订阅,那个时候李四将收不到布告
    bridegroom.MarryEvent-=new MarryHandler(friend2.SendMessage);
    bridegroom.MarryEvent+=new MarryHandler(friend3.SendMessage);
    bridegroom.OnMarriageComing("Friend,I Will Marry!!");
    Console.ReadKey();
  }
}

using System;

public class Friend
{
  public string Name;
  public Friend(string name)
  {
    Name=name;
  }
  //事件处理函数,该函数要求切合MarryHandler委托的概念
  public void SendMessage(object s,MarryEventArgs e)
  {
    Console.WriteLine(e.Message);
    Console.WriteLine(this.Name+"收到了,届期候按期参与"卡塔尔国;
  }
}

using System.Windows.Forms;

透过自定义Marry伊芙ntArgs事件类扩充了伊夫ntArgs类,那时候MarryEventArgs带有多少个名称为Message的事件参数;然后在订阅对象的SendMessage方法中,通过e.Message的办法拿到了平地风波数量,并把事件数量输出。

public class ActionStudy

 

{

3、事件的庐山面目目

   public static void Main()

从以上的事例大家能够知晓,事件是在信托的幼功之上的。那么,它们毕竟有着哪些的关系啊,这些就必得通过Reflector来窥伺者了。

   {

简单的说的源代码:

      Action<string> messageTarget;

namespace 窥伺者事件真相
{
  class Program
  {
    public delegate void MarryHanler(string msg);

      if (Environment.GetCommandLineArgs().Length > 1)

    public event MarryHanler MarryEvent;
    static void Main(string[] args)
    {
    }
  }
}

         messageTarget = s => MessageBox.Show(s);

Reflector反编译的结果:

      else

图片 2

         messageTarget = s => Console.WriteLine(s);

图1

      messageTarget("Hello, World!");

 

   }

图片 3

}

图2

下边包车型客车躬体力行演示怎么着使用 Action(T卡塔尔(英语:State of Qatar) 委托来打字与印刷 List(T) 对象的内容。在那示例中,使用 Print 方法将列表的剧情体现到调整台上。别的,C# 示例还亲自去做怎么样选拔匿超形式将内容体现到调整台上。

 

using System;

图片 4

using System.Collections.Generic;

图3

class Program

图片 5

{

图4

    static void Main()

能够见到,C#事件被编译成包涵四个集体措施的代码段,三个饱含add_前缀,另四个带有remove_前缀,前缀前面是C#事件的称谓。

    {

在add_主意中,通过调用了Delegate.Combine(卡塔尔方法来兑现的(图3中红框的地点),Delegate.Combine(卡塔尔(英语:State of Qatar)方法将多个委托组合为了二个多路广播委托。

        Action<string> PrintInConsole = s => Console.WriteLine(s);

在remove_主意中,同样利用了Delegate.Remove(卡塔尔国方法。

        Action<string> PrintInDialog = s=>MessageBox.Show(s);

由地点的四张图中得以计算出:

        List<String> names = new List<String>();

C#的风云是叁个奇特的多路广播委托,事件暗中认可含有一个私人民居房的嘱托项目变量(图2的红框),该变量用于保存对事件管理方法的引用,且该信托项目标变量为个体,只好从概念该事件的类中展开访谈。

        names.Add("Bruce");

从反编写翻译的代码中得以见到跟大家学过的品质是平日的。但与事件不一致,属性中定义了set访问和get访谈器,多个访谈器的面目就是以"get_"和"set_"为前缀的七个办法。属性用于对类中的私有字段举行拜候,而C#事件也能够当作是“委托字段的性质”,由此得以经过事件来对个人的信托字段张开访谈,那也是C#事件天性存在的案由。C#事件机制相符面向对象的封装天性,是代码更安全。

        names.Add("Alfred");

        names.Add("Tim");

        names.Add("Richard");

        names.ForEach(PrintInConsole);

        names.ForEach(PrintInDialog);      

    }

}

版权声明:本文由bob体育app发布于编程,转载请注明出处:事件的解密