事件的解密,params关键字修饰的参数的匿名委托和Lambda表达式

By admin in 4858.com on 2019年3月25日

委托

原为出处:

 事件概述                                                           

在前方一篇中写到了信托,也说了信托是C#中许多特点的基础,那篇要讲的轩然大波,正是树立在委托之上的。在C#1.0中,委托和事件是最要害的四个特色。

信托与事件,平常拿来就用,往往忘记其促成原理,对其接纳方法也特别局限。周家安先生在《C#
6.0
学习笔记》中对信托和事件的讲解,深切浅出,清晰明了,故专程摘抄一篇小说,勤看勤思。

在.NET在,大家平时使用委托,委托的意义不必多说,在.NET
2.0事先,大家在动用委托以前,得自定义一个委托项目,再利用那么些自定义的委托类型定义一个寄托字段或变量。.NET
2.0给我们带来了Action、Func四个泛型委托,.NET3.0给大家带来了Lambda,这一体使得委托的概念和行使变得不难起来。下边包车型大巴例子中的委托都施用了拉姆da表达式。

   
 在发生其余类或对象关注的事体时,类或对象可透过事件通报它们。发送(或吸引)事件的类称为“发行者”,接收(或拍卖)事件的类称为“订户”。

一 、什么是事件?

首先,委托是一种情势上与办法签名相似的类型。

一.Action种种的泛型委托

  • 特点
    • 发行者明确什么日期引发风云,订户分明实施何种操作来响应该事件。
    • 二个风云能够有八个订户。3个订户可处理来自五个发行者的三个事件。
    • 尚无订户的事件永远不会被调用。
    • 事件不足为奇用于公告用户操作
    • 只要三个事变有三个订户,当引发该事件时,会一起调用八个事件处理程序,也得以设置异步调用事件。
    • 能够行使事件联合线程。
    • 事件是依照 伊芙ntHandler 委托和
      伊夫ntArgs 基类的。

事件设计到两类剧中人物——事件发表者和事件订阅者。当有个别事件发生后,事件揭橥者会宣布新闻;事件订阅者会接到到信息,并做出相应的拍卖,那正是事件的进度。

概念五个信托:

Action种类的委托定义的是未曾再次回到值(再次来到值为void)的嘱托。它有八个版本包涵没有输入参数,1个输入参数,三个输入参数,3个输入参数,五个输入参数共伍个本子那多少个本子的原型如下:

 事件的订阅和注销订阅                                       

 

public delegate void DoSome(string msg);

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

     假诺您想编写引发风云时调用的自定义代码,则能够订阅由别的类发布的事件。例如,能够订阅有些按钮的“单击”事件,以使应用程序在用户单击该按钮时实施一些实惠的操作。

② 、使用事件

 

Action委托 封装多个艺术,该措施不选取参数并且不再次回到值。

  • 订阅事件
    • VS IDE 订阅事件
      • 即使“属性”窗口不可知,请在“设计”视图中,右击要开创事件处理程序的窗体或控件,然后选用“属性”。
      • 在“属性”窗口的顶部,单击“事件”图标。
      • 双击要开创的风云,Visual C#
        会创制四个空事件处理程序方法,并将其添加到您的代码中。只怕,您也足以在“代码”视图中手动添加代码。
    • 编制程序格局订阅事件

      • 概念三个事件处理程序方法,其签名与该事件的委托签名匹配。例如,假设事件基于
        伊夫ntHandler 委托类型,则上边的代码表示方法存根

2.1 定义事件

利用首要字 delegate, 类型名称为 DoSome(string msg).

能够采纳此委托以参数情势传递四个进行某操作的点子,而不用显式声明一(Wissu)个自定义的嘱托来封装此方法。该包裹的法门必须与此委托定义的章程签名相对应。那意味该格局不得持有参数和重返值。例:

void HandleCustomEvent(object sender, CustomEventArgs a){  }

在C#中定义事件和定义类的积极分子是很一般的,只要2个event关键字就足以了。比如:

 

using System;

      • 行使加法赋值运算符 (+=)
        来为事件附加事件处理程序。在底下的演示中,假如名为 publisher
        的对象拥有二个名为 RaiseCustom伊芙nt
        的风浪。请留心,订户类必要引用发行者类才能订阅其事件。

public event EventHandler birthday;

 

using System.Windows.Forms;

publisher.RaiseCustomEvent += HandleCustomEvent;
publisher.RaiseCustomEvent += new CustomEventHandler(HandleCustomEvent);

中间event是重点字,而伊芙ntHandler是信托项目。

创立三个DoSome(string
msg)委托项目对象,并实例化。

public class Name

    • 匿名形式订阅事件
      • 采用加法赋值运算符 (+=)
        来为事件附加匿名格局。在底下的以身作则中,假使名为 publisher
        的对象拥有叁个名为 RaiseCustom伊夫nt 的风浪,并且还定义了一个Custom伊芙ntArgs
        类以承载有些品种的专用事件音信。请留意,订户类需求引用
        publisher 才能订阅其事件。

所以能够把事件定义的组织总计为:访问修饰符 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;

{

publisher.RaiseCustomEvent += delegate(object o, CustomEventArgs e)
{
    string s = o.ToString() + " " + e.ToString();
    Console.WriteLine(s);
};

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);
  //使用自定义委托类型定义事件,事件名为Marry伊夫nt
  public event MarryHandler MarryEvent;

输出:

   public Show()

     使用减法赋值运算符 (-=)
废除订阅事件。全体订户都收回订阅某事件后,发行者类中的事件实例会设置为
null。

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

4858.com 1

{

publisher.RaiseCustomEvent -= HandleCustomEvent;

  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();

     上边包车型地铁历程演示了何等将符合标准 .NET
Framework 情势的轩然大波添加到您本身的类和结构中。.NET Framework
类库中的全数事件均依照 伊芙ntHandler 委托,定义如下。

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

 事件自己便是寄托项目。

}

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

    //发出文告,此时唯有订阅了轩然大波的靶子才能接受文告
    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)

  • 应用 伊夫ntHandler
    形式公布事件
    • (假使不需求发送含事件的自定义数据,请跳过此步骤,间接进去步骤
      3。)在发行者类和订户类均可尽收眼底的限量中注解类,并丰裕保留自定义事件数量所需的积极分子。在此示例中,会回到二个简易字符串。

    //使用”-=”来撤销事件订阅,此时李四将收不到通告
    bridegroom.MarryEvent-=new MarryHandler(friend2.SendMessage);

日常,作为事件委托,有八个参数,一个是Object类型,表示引发轩然大波的靶子,就是什么人引发了事件的,多数状态下在调用事件时是把类的日前实例引用(this)传递过去。另二个参数是从System.伊芙ntArgs派省的类的实例。那是叁个正式的事件处理程序的签订契约,为了规范事件的拍卖,.NET类库已经定义好四个System.伊芙ntHandler委托,用于注明事件。它的原型如下:

   {

public class CustomEventArgs : EventArgs
{
    public CustomEventArgs(string s)
    {
        msg = s;
    }
    private string msg;
    public string Message
    {
        get { return msg; }
    } 
}

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

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

      this.instanceName = name;

    • (若是你使用的是 伊芙ntHandler
      的泛型版本,请跳过此步骤。)在昭示类中扬言一个委托。为它钦赐以
      伊夫ntHandler 结尾的名目。第一个参数钦赐自定义 伊芙ntArgs
      类型。

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

 引发风云的目的实例将传递给sender参数,而与事件相关的多少则传递给e参数。假若不须要传递过多的多寡,能够透过System.EventArgs.Empty静态成员重返三个空的伊夫ntArgs对象类传递。

   }

public delegate void CustomEventHandler(object sender, CustomEventArgs a);

    Console.ReadKey();
  }
}

唯独,由于分化的轩然大波要传送的参数区别,更多时候是从伊芙ntArgs类派生的子类的实例,显著2个伊夫ntHandler委托是不能够满足各样情状的。假若针对差别的轩然大波也顶一个贰个应和的嘱托,水量一旦多起来,既混乱,也糟糕管理。为了化解那一个难题,.NET类库又提供了1个含有泛型参数的事件处理委托。原型如下:

   public void DisplayToConsole()

    • 使用以下任一步骤,在揭发类中宣示事件。
      • 设若没有自定义 伊芙ntArgs
        类,事件类型正是非泛型 伊芙ntHandler
        委托。它无需证明,因为它已在 C# 项目私下认可包括的 System
        命名空间中开始展览了声称

  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);

   {

public event EventHandler RaiseCustomEvent;

值得注意的是,事件处理函数的定义必要与自定义的委托定义保持一致,即参数个数,参数类型和再次回到类型等须求与信托同一。

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

      Console.WriteLine(this.instanceName);

      • 万一运用的是 伊芙ntHandler
        的非泛型版本,并且您有3个由 伊夫ntArgs
        派生的自定义类,请在发布类中扬言您的事件,并且将您的嘱托用作类型

除此而外选择自定义委托项目来定义事件外,仍是能够使用.NET类库中预约义的寄托项目伊芙ntHandler来定义事件,须求注意它们的参数。

泛型参数的风浪,实例:

   }

class Publisher
{
    public event CustomEventHandler RaiseCustomEvent;
}

public class Bridegroom
{
  //使用.NET类库中的类型定义事件,事件名为Marry伊夫nt
事件的解密,params关键字修饰的参数的匿名委托和Lambda表达式。  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()

      • 一旦运用的是泛型版本,则不要求自定义委托。相反,应将事件类型钦赐为
        伊夫ntHandler<Custom伊芙ntArgs>,在尖括号内放置您本身的类的名目。

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

 

   {

public event EventHandler<CustomEventArgs> RaiseCustomEvent;

  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(“王五”);

   }

   
 以下不难示例演示了在基类中宣示可从派生类引发的轩然大波的正式措施。此方式广泛应用于
.NET Framework 基类库中的 Windows 窗体类。

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

}

     在创建可用作别的类的基类的类时,必须考虑如下事实:事件是特殊类型的寄托,只可以够从表明它们的类中调用。派生类不可能直接调用基类中宣示的风浪。就算有时你可能希望某些事件只可以通过基类引发,但在大部景况下,您应该允许派生类调用基类事件。为此,您能够在含有该事件的基类中开创3个受保证的调用方法。通过调用或重写此调用方法,派生类便足以直接调用该事件。

    //发出文告,此时只有订阅了风云的靶子才能接过公告
    bridegroom.OnMarriageComing(“Friend,I Will Marry!!”);
    Console.WriteLine(“————————————“);

public class ActionStudy

namespace BaseClassEvents
{
    using System;
    using System.Collections.Generic;
    public class ShapeEventArgs : EventArgs
    {
        private double newArea;

        public ShapeEventArgs(double d)
        {
            newArea = d;
        }
        public double NewArea
        {
            get { return newArea; }
        }
    }
    public abstract class Shape
    {
        protected double area;

        public double Area
        {
            get { return area; }
            set { area = value; }
        }
        public event EventHandler<ShapeEventArgs> ShapeChanged;
        public abstract void Draw();
        protected virtual void OnShapeChanged(ShapeEventArgs e)
        {
            EventHandler<ShapeEventArgs> handler = ShapeChanged;
            if (handler != null)
            {
                handler(this, e);
            }
        }
    }
    public class Circle : Shape
    {
        private double radius;
        public Circle(double d)
        {
            radius = d;
            area = 3.14 * radius;
        }
        public void Update(double d)
        {
            radius = d;
            area = 3.14 * radius;
            OnShapeChanged(new ShapeEventArgs(area));
        }
        protected override void OnShapeChanged(ShapeEventArgs e)
        {
            base.OnShapeChanged(e);
        }
        public override void Draw()
        {
            Console.WriteLine("Drawing a circle");
        }
    }
    public class Rectangle : Shape
    {
        private double length;
        private double width;
        public Rectangle(double length, double width)
        {
            this.length = length;
            this.width = width;
            area = length * width;
        }
        public void Update(double length, double width)
        {
            this.length = length;
            this.width = width;
            area = length * width;
            OnShapeChanged(new ShapeEventArgs(area));
        }
        protected override void OnShapeChanged(ShapeEventArgs e)
        {
            base.OnShapeChanged(e);
        }
        public override void Draw()
        {
            Console.WriteLine("Drawing a rectangle");
        }

    }
    public class ShapeContainer
    {
        List<Shape> _list;

        public ShapeContainer()
        {
            _list = new List<Shape>();
        }

        public void AddShape(Shape s)
        {
            _list.Add(s);
            s.ShapeChanged += HandleShapeChanged;
        }
        private void HandleShapeChanged(object sender, ShapeEventArgs e)
        {
            Shape s = (Shape)sender;
            Console.WriteLine("Received event. Shape area is now {0}", e.NewArea);
            s.Draw();
        }
    }
    class Test
    {

        static void Main(string[] args)
        {
            Circle c1 = new Circle(54);
            Rectangle r1 = new Rectangle(12, 9);
            ShapeContainer sc = new ShapeContainer();
            sc.AddShape(c1);
            sc.AddShape(r1);
            c1.Update(57);
            r1.Update(7, 7);
            Console.WriteLine();
            Console.WriteLine("Press Enter to exit");
            Console.ReadLine();
        }
    }
}

    //使用”-=”来撤销事件订阅,此时李四将收不到布告
    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();

public interface IDrawingObject
{
    event EventHandler ShapeChanged;
}
public class MyEventArgs : EventArgs {…}
public class Shape : IDrawingObject
{
    event EventHandler ShapeChanged;
    void ChangeShape()
    {
        // Do something before the event…
        OnShapeChanged(new MyEventsArgs(…));
        // or do something after the event. 
    }
    protected virtual void OnShapeChanged(MyEventArgs e)
    {
        if(ShapeChanged != null)
        {
           ShapeChanged(this, e);
        }
    }
}

伊芙ntHandler是.NET类库中预约义的信托项目,用于拍卖不分包事件数量的风云。使用Reflector来查阅伊夫ntHandler的有血有肉定义:

      testName.Show();

     下边的言传身教演示怎么样处理以下的不常见情形:您的类是从五个以上的接口继承的,每一种接口都包蕴同名事件)。在那种地方下,您至少要为个中3个风云提供显式接口达成。为事件编写显式接口完结时,必须编写制定add 和 remove
事件访问器。那四个事件访问器日常由编写翻译器提供,但在这种情形下编写翻译器无法提供。

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

   }

     您能够提供本身的访问器,以便钦命这七个事件是由你的类中的同一事件表示,依然由不一样事件代表。例如,根据接口规范,假若事件应在区别时间引发,则足以将各类事件与类中的3个单身达成关系。在底下的以身作则中,订户将造型引用强制转换为
IShape 或 IDrawingObject,从而鲜明本人将会吸收接纳哪个 OnDraw 事件。

4858.com ,从概念中能够看来,该委托类型的回到类型为void,第二个参数sender负责保存触发事件指标的引用,其体系为object;第四个参数e负责保存事件数量。伊芙ntArgs类也是.NET类库中定义的类,它不保留任何数据,假设想在事变中包括事件数量,就亟须接纳EventArgs的派生类来达成。

}

namespace WrapTwoInterfaceEvents
{
    using System;
    public interface IDrawingObject
    {
        event EventHandler OnDraw;
    }
    public interface IShape
    {
        event EventHandler OnDraw;
    }
    public class Shape : IDrawingObject, IShape
    {
        event EventHandler PreDrawEvent;
        event EventHandler PostDrawEvent;
        event EventHandler IDrawingObject.OnDraw
        {
            add { PreDrawEvent += value; }
            remove { PreDrawEvent -= value; }
        }
        event EventHandler IShape.OnDraw
        {
            add { PostDrawEvent += value; }
            remove { PostDrawEvent -= value; }
        }
        public void Draw()
        {
            EventHandler handler = PreDrawEvent;
            if (handler != null)
            {
                handler(this, new EventArgs());
            }
            Console.WriteLine("Drawing a shape.");
            handler = PostDrawEvent;
            if (handler != null)
            {
                handler(this, new EventArgs());
            }
        }
    }
    public class Subscriber1
    {
        public Subscriber1(Shape shape)
        {
            IDrawingObject d = (IDrawingObject)shape;
            d.OnDraw += new EventHandler(d_OnDraw);
        }
        void d_OnDraw(object sender, EventArgs e)
        {
            Console.WriteLine("Sub1 receives the IDrawingObject event.");
        }
    }
    public class Subscriber2
    {
        public Subscriber2(Shape shape)
        {
            IShape d = (IShape)shape;
            d.OnDraw += new EventHandler(d_OnDraw);
        }

        void d_OnDraw(object sender, EventArgs e)
        {
            Console.WriteLine("Sub2 receives the IShape event.");
        }
    }
    public class Program
    {
        static void Main(string[] args)
        {
            Shape shape = new Shape();
            Subscriber1 sub = new Subscriber1(shape);
            Subscriber2 sub2 = new Subscriber2(shape);
            shape.Draw();

            Console.WriteLine("Press Enter to close this window.");
            Console.ReadLine();
        }
    }
}

2.3 扩展EventArgs类

2.       有二个输入参数重临值为void的信托

 使用字典存储事件实例                                       

上边说了,假诺要在事变中隐含事件数量,就不可能不运用伊夫ntArgs的派生类。具体的落实代码如下:

Action<T>泛型委托封装三个情势,该方式只使用1个参数并且不重临值。

     accessor-declarations
的一种用法是理解大气的事件但不为每种事件分配字段,而是采纳字典来囤积那个事件实例。那唯有在具备非凡多的事件、但您揣度超过5/10风浪都不会落到实处时才有用。

public class
MarryEventArgs:EventArgs

{
  public string Message;
  public MarryEventArgs(string msg)
  {
    Message=msg;
  }
}

能够选拔此委托以参数情势传递形式,而不用显式证明自定义的嘱托。该办法必须与此

public delegate void EventHandler1(int i);
public delegate void EventHandler2(string s);
public class PropertyEventsSample
{
    private System.Collections.Generic.Dictionary<string, System.Delegate> eventTable;
    public PropertyEventsSample()
    {
        eventTable = new System.Collections.Generic.Dictionary<string, System.Delegate>();
        eventTable.Add("Event1", null);
        eventTable.Add("Event2", null);
    }
    public event EventHandler1 Event1
    {
        add
        {
            eventTable["Event1"] = (EventHandler1)eventTable["Event1"] + value;
        }
        remove
        {
            eventTable["Event1"] = (EventHandler1)eventTable["Event1"] - value;
        }
    }
    public event EventHandler2 Event2
    {
        add
        {
            eventTable["Event2"] = (EventHandler2)eventTable["Event2"] + value;
        }
        remove
        {
            eventTable["Event2"] = (EventHandler2)eventTable["Event2"] - value;
        }
    }
    internal void RaiseEvent1(int i)
    {
        EventHandler1 handler1;
        if (null != (handler1 = (EventHandler1)eventTable["Event1"]))
        {
            handler1(i);
        }
    }
    internal void RaiseEvent2(string s)
    {
        EventHandler2 handler2;
        if (null != (handler2 = (EventHandler2)eventTable["Event2"]))
        {
            handler2(s);
        }
    }
}
public class TestClass
{
    public static void Delegate1Method(int i)
    {
        System.Console.WriteLine(i);
    }
    public static void Delegate2Method(string s)
    {
        System.Console.WriteLine(s);
    }
    static void Main()
    {
        PropertyEventsSample p = new PropertyEventsSample();

        p.Event1 += new EventHandler1(TestClass.Delegate1Method);
        p.Event1 += new EventHandler1(TestClass.Delegate1Method);
        p.Event1 -= new EventHandler1(TestClass.Delegate1Method);
        p.RaiseEvent1(2);

        p.Event2 += new EventHandler2(TestClass.Delegate2Method);
        p.Event2 += new EventHandler2(TestClass.Delegate2Method);
        p.Event2 -= new EventHandler2(TestClass.Delegate2Method);
        p.RaiseEvent2("TestString");
    }
}

public class Bridegroom
{
  //自定义委托项目,委托包括七个参数
  public delegate void MarryHandler(object sender,MarryEventArgs e);
  //使用自定义委托类型定义事件,事件名为Marry伊芙nt
  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;

     有二种办法可向客户端代码公开异步功用。基于事件的异步方式为类规定了用来映现异步行为的提议措施。对于相对不难的八线程应用程序,BackgroundWorker
组件提供了1个简易的缓解方案。对于更复杂的异步应用程序,请考虑落成2个适合基于事件的异步形式的类。

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类,此时Marry伊芙ntArgs带有一个名为Message的事件参数;然后在订阅对象的SendMessage方法中,通过e.Message的主意获得了轩然大波数量,并把事件数量输出。

public class ActionStudy

 

{

三 、事件的实质

   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

4858.com 2

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

图1

      messageTarget(“Hello, World!”);

 

   }

4858.com 3

}

图2

下边的以身作则演示怎么着运用 Action(T) 委托来打字与印刷 List(T) 对象的剧情。在此示例中,使用 Print 方法将列表的内容体现到控制台上。其它,C# 示例还演示如何运用匿超级模特式将内容呈现到控制台上。

 

using System;

4858.com 4

using System.Collections.Generic;

图3

class Program

4858.com 5

{

图4

    static void Main()

可以观察,C#事件被编写翻译成包罗多少个国有措施的代码段,1个分包add_前缀,另五个暗含remove_前缀,前缀前边是C#事件的名号。

    {

在add_情势中,通过调用了Delegate.Combine()方法来落到实处的(图3中红框的地方),Delegate.Combine()方法将四个委托组合为了3个多路广播委托。

        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);      

    }

}

3.       有二个输入参数再次回到值为void的委托

Action<T1,T2> 封装三个方法,该措施具有多少个参数并且不重临值。

能够选用 Action(T1,
T2
) 委托以参数格局传递格局,而不用显式申明自定义的嘱托。该

格局必须与此委托定义的不二法门签名相对应。也正是说,封装的方法必须具备多少个均经过值传递给它的参数,并且不能够重回值。

using System;

using System.IO;

public class ActinStudy

{

   public static void Main()

   {

      string message1 = “The first line of a message.”;

      string message2 = “The second line of a message.”;

      Action<string, string>  concat;

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

         concat = (s1, s2) =>

{

StreamWriter writer = null; 

      try

      {

         writer = new
StreamWriter(Environment.GetCommandLineArgs()[1], false);

         writer.WriteLine(“{0}”n{1}”, s1, s2);

      }

      catch

      {

         Console.WriteLine(“File write operation failed…”);

      }

      finally

      {

         if (writer != null) writer.Close();

      }

};

      else

         concat = (s1, s2) => Console.WriteLine(“{0}”n{1}”, s1, s2);

      concat(message1, message2);

   }

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

Action<T1,T2,T3>委托,封装三个格局,该措施应用四个参数并且不返回值。

可以采用 Action(T1, T2,
T3
) 委托以参数方式传递格局,而不用显式注脚自定义的寄托。

该情势必须与此委托定义的措施签名相呼应。也便是说,封装的艺术必须具备四个均经过值传递给它的参数,并且不能够再次来到值。

5.       有5个输入参数重回值为void的嘱托

Action<T1,T2,T3,T4>委托, 封装一个格局,该办法具有多少个参数并且不再次回到值。

能够运用 Action(T1, T2, T3,
T4
) 委托以参数情势传递格局,而不用显式申明自定义的嘱托。封装的点子必须与此委托定义的主意签名相呼应。也正是说,封装的法门必须持有三个均经过值传递给它的参数,并且无法重回值。

二.Func系统的泛型委托

Func类别的寄托定义的是重回值的寄托。它有四个本子包罗没有输入参数,二个输入参数,三个输入参数,叁个输入参数,四个输入参数共陆个版本那多少个本子的原型如下:

1.       没有输入参数有重临值(再次来到值不为void)的寄托

Func<TResult>封装三个不有所参数但却再次回到 TResult 参数钦点的类型值的点子。
能够利用此委托构造二个能以参数形式传递的措施,而不用显式注脚自定义的委托。该

主意必须与此委托定义的法子签名相对应。那代表封装的点子不得持有参数,但无法不再次来到值。

2.       具有2个输入参数有再次回到值(重临值不为void)的寄托

  
Func<T,TResult>封装一个享有贰个参数并回到 TResult 参数钦赐的类型值的办法。

能够应用此委托构造二个能以参数方式传递的不二法门,而不用显式评释自定义的委托。该方法必须与此委托定义的方法签名相对应。也正是说,封装的法子必须拥有1个透过值传递给它的参数,并且必须再次回到值。

3.       具有三个输入参数有再次回到值(再次回到值不为void)的委托

  Func<T1,T2,TResult>封装3个怀有三个参数并回到 TResult 参数钦点的类型值的办法。

能够接纳此委托构造1个能以参数情势传递的不二法门,而不用显式注脚自定义的信托。该措施必须与此委托定义的方法签名相呼应。也正是说,封装的法子必须有所七个均经过值传递给它的参数,并且必须重返值

4.       具有五个输入参数有重临值(重回值不为void)的委托

  
Func<T1,T2,T3,TResut>封装多个拥有两个参数并回到 TResult 参数钦赐的类型值的点子。

能够行使此委托构造三个能以参数情势传递的不二法门,而不用显式证明自定义的寄托。该方法必须与此委托定义的方式签名相对应。也正是说,封装的方法必须具备多个均通过值传递给它的参数,并且必须重返值。

5.       具有八个输入参数有再次回到值(重临值不为void)的寄托

 Func<T1,T2,T3,TResult>封装2个享有八个参数并回到 TResult 参数内定的类型值的方法。

能够使用此委托构造一个能以参数格局传递的法门,而不用显式注解自定义的信托。该措施必须与此委托定义的措施签名相对应。也便是说,封装的艺术必须具备八个均通过值传递给它的参数,并且必须重临值。

三. EventHandler(TEventArgs) 泛型委托

EventHandler(TEventArgs) 是一种预约义委托,表示事件的事件处理程序方法,它与事件是还是不是变动事件数量无关。假诺事件不扭转事件数量,则用EventArgs 替代泛型类型参数;不然,提供温馨的自定义事件数据类型并用该项目替代泛型类型参数。

采用 伊夫ntHandler<(Of
<(T伊夫ntArgs>)>) 的长处在于,如若事件生成事件数量,则无需编写自个儿的自定义委托代码。其余,.NET
Framework 只需二个贯彻就能支撑 伊芙ntHandler<(Of
<(T伊夫ntArgs>)>),那与代表泛型类型参数的轩然大波数据类型无关。

若要将事件与处管事人件的主意关联,请向事件添加委托的实例。除非移除了该信托,不然每当产生该事件时就调用事件处理程序。

咱俩掌握,.NET
Framework 中的事件模型基于已有事件委托,该信托将事件与事件处理程序连接。引发风浪供给多个元素:

①     引用向事件提供响应的措施的寄托。

②保留事件数量的类。

下边是MSDN上的3个简便的例证:

上面包车型客车代码示例申明事件数量和使用该事件数量的 伊夫ntHandler<(Of
<(T伊夫ntArgs>)>) 泛型委托,并演示怎么着吸引该事件。

using System;

using System.Collections.Generic;

//首先定义事件处理参数:那个参数派生于伊芙ntArgs

public class MyEventArgs : EventArgs

{

    private string msg;

    public MyEventArgs( string messageData ) { msg = messageData;}

    public string Message {

        get { return msg; }

        set { msg = value; }

    }

}

//上面定义事件处理类,注意那里运用了伊芙ntHandler<T>委托项目

public class HasEvent

{

// Declare an event of delegate type EventHandler of

// MyEventArgs.

    public event EventHandler<MyEventArgs> SampleEvent;

    public void DemoEvent(string val)

    {

        //复制到暂且变量,以保障线程安全

        EventHandler<MyEventArgs> temp = SampleEvent;

        if (temp != null)

            temp(this, new MyEventArgs(val));

    }

}

若是在在此以前的老艺术则上边包车型大巴类则应该写为:

//首先得定义3个委托

public delegate void MyEventHandler(Object sender, MyEventArgs args);

public class HasEvent

{

// Declare an event of delegate type EventHandler of

// MyEventArgs.

    public event MyEventHandler  SampleEvent;

    public void DemoEvent(string val)

    {

        //复制到临时变量,以保证线程安全

        MyEventHandler  temp = SampleEvent;

        if (temp != null)

            temp(this, new MyEventArgs(val));

    }

}

看得出使用那么些泛型委托精简了好多代码.

//-上面的类订阅了地方定义的轩然大波

public class Sample

{

    public static void Main()

    {

        HasEvent he = new HasEvent();

        he.SampleEvent +=

new ventHandler<MyEventArgs>((object src,MyEventArgs mea)
=> MessageBox.Show(mea.Message));//那里运用了Lambda表明式

        he.DemoEvent(“Hey there, Bruce!”);

        he.DemoEvent(“How are you today?”);

        he.DemoEvent(“I’m pretty good.”);

        he.DemoEvent(“Thanks for asking!”);

    }

}

四.params参数类型的寄托

在地点介绍的Action和Func种类的信托中,不能运用由params关键字修改的参数,作者自然想用params关键字来贯彻可变类型的参数,结果发现行反革命不通,那时只可以用生成类型的信托,不过请小心在用匿名格局绑定具有params关键字修饰的参数的寄托时,匿有名的模特式的参数中却不能有params关键字,在匿名情势中唯有去掉了params关键字,即便从匿名格局中去掉了params关键字,但那并不要紧碍行使相同的语法.

比如注解了如下类型的委托

public void delegate DelegateWithParamskeyword(params object[] param);

可是你去不能用上边包车型的士不二法门去绑定1个匿名方式:

DelegateWithParamskeyword dwp = delegate(params object[] param)

{
 foreach(object o in param)

 {

    Console.WriteLine(o.ToString());

 }

}

或者

DelegateWithParamskeyword dwpkw = (params object[] param) =>
{foreach(object o in param)MessageBox.Show(o.ToString());};

而相应去掉上边匿超格局中的params关键字.

DelegateWithParamskeyword dwp = delegate(object[] param)

{
 foreach(object o in param)

 {

    Console.WriteLine(o.ToString());

 }

}

或者

DelegateWithParamskeyword dwpkw = (object[] param) =>
{foreach(object o in param) MessageBox.Show(o.ToString());};

就好像上边说的那并不影响你利用同样的的语法来调用委托

如: dwp(1,2,”x”,”y”,”dog”,9.28);

dwpkw(“dog”,”xy”,10);

为此,能够将匿名格局绑定到二个用到params申明的寄托,那样一来,就能够在调用时传出你期望的肆意数目标参数。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图
Copyright @ 2010-2019 美高梅手机版4858 版权所有