委托

原为出处:

 事件概述                                                           

在前边一篇中写到了寄托,也说了寄托是C#中很多表征的底子,那篇要讲的事件,正是建立在信托之上的。在C#一.0中,委托和事件是最紧要的七个特点。

信托与事件,平时拿来就用,往往忘记其促成原理,对其选拔方法也更为局限。周家安先生在《C#
六.0
学习笔记》中对信托和事件的执教,深刻浅出,清晰明了,故专程摘抄1篇随笔,勤看勤思。

在.NET在,大家平时利用委托,委托的功能不必多说,在.NET
二.0事先,大家在采取委托在此之前,得自定义3个委托项目,再利用那么些自定义的嘱托类型定义三个信托字段或变量。.NET
二.0给我们带来了Action、Func七个泛型委托,.NET三.0给大家带来了拉姆da,那1切使得委托的概念和应用变得不难起来。上边包车型大巴事例中的委托都使用了Lambda表明式。

   
 在产生其他类或对象关怀的工作时,类或对象可透过事件通报它们。发送(或吸引)事件的类称为“发行者”,接收(或处理)事件的类称为“订户”。

1、什么是事件?

首先,委托是1种方式上与措施签名相似的类型。

壹.Action密密麻麻的泛型委托

  • 特点
    • 发行者明确几时引发轩然大波,订户明确实施何种操作来响应该事件。
    • 2个风云能够有五个订户。三个订户可处理来自几个发行者的多个事件。
    • 不曾订户的风云永远不会被调用。
    • 事件司空见惯用于文告用户操作
    • 万1八个事件有多少个订户,当引发该事件时,会联合调用三个事件处理程序,也能够安装异步调用事件。
    • 能够应用事件联合线程。
    • 事件是依照 伊夫ntHandler 委托和
      伊夫ntArgs 基类的。

事件设计到两类剧中人物——事件宣布者和事件订阅者。当某些事件时有爆发后,事件公布者会揭穿音讯;事件订阅者会接受到新闻,并做出相应的处理,这就是事件的经过。

概念贰个寄托:

Action类别的寄托定义的是从未再次回到值(重临值为void)的委托。它有两个本子包蕴未有输入参数,二个输入参数,二个输入参数,一个输入参数,多少个输入参数共多个本子这多少个本子的原型如下:

 事件的订阅和注销订阅                                       

 

public delegate void DoSome(string msg);

一.       未有输入参数重临值为void的委托.

     就算您想编写引发轩然大波时调用的自定义代码,则足以订阅由别的类公布的事件。例如,能够订阅有些按钮的“单击”事件,以使应用程序在用户单击该按钮时实施壹些立见效用的操作。

二、使用事件

 

Action委托 封装一个方法,该措施不使用参数并且不重返值。

  • 订阅事件
    • VS IDE 订阅事件
      • 假如“属性”窗口不可见,请在“设计”视图中,右击要开创事件处理程序的窗体或控件,然后采取“属性”。
      • 在“属性”窗口的顶部,单击“事件”图标。
      • 双击要创建的风浪,Visual C#
        会成立贰个空事件处理程序方法,并将其添加到您的代码中。也许,您也能够在“代码”视图中手动添加代码。
    • 编制程序格局订阅事件

      • 概念四个事件处理程序方法,其签名与该事件的寄托签名相称。例如,要是事件基于
        伊芙ntHandler 委托类型,则下边的代码表示方法存根

二.一 定义事件

应用主要字 delegate, 类型名字为 DoSome(string msg).

能够动用此委托以参数方式传递多少个实践某操作的法子,而不用显式声贝拉米(Bellamy)个自定义的委托来封装此方法。该包装的办法必须与此委托定义的点子签名相呼应。那意味该格局不得持有参数和重返值。例:

void HandleCustomEvent(object sender, CustomEventArgs a){  }

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

 

using System;

      • 动用加法赋值运算符 (+=)
        来为事件附加事件处理程序。在底下的演示中,尽管名称叫 publisher
        的目的拥有1个名字为 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
        的靶子具备1个名字为 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);
};

二.贰 订阅和收回事件

 委托列表

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

金沙注册送58 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     }

params关键字修饰的参数的匿名委托和Lambda表明式,事件的解密。   public Name(string name)

  • 运用 伊芙ntHandler
    情势发表事件
    • (若是不须要发送含事件的自定义数据,请跳过此步骤,间接进入步骤
      叁。)在发行者类和订户类均可尽收眼底的限制中注脚类,并丰硕保留自定义事件数量所需的分子。在此示例中,会回去1个简短字符串。

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

1般说来,作为事件委托,有多个参数,贰个是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.伊芙ntArgs.Empty静态成员再次回到四个空的伊芙ntArgs对象类传递。

   }

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

    Console.ReadKey();
  }
}

可是,由于不相同的风浪要传送的参数差别,更加多时候是从伊芙ntArgs类派生的子类的实例,明显二个伊夫ntHandler委托是不能够满意各个情状的。假使针对差异的风云也顶3个二个应和的嘱托,水量1旦多起来,既混乱,也倒霉管理。为了缓解那些题材,.NET类库又提供了二个暗含泛型参数的事件处理委托。原型如下:

   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;

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

T伊夫ntArgs
是二个泛型参数,应该是System.伊芙ntArgs类恐怕System.伊夫ntArgs类的派生类型。

      Console.WriteLine(this.instanceName);

      • 若是选用的是 伊芙ntHandler
        的非泛型版本,并且您有几个由 伊夫ntArgs
        派生的自定义类,请在揭露类中宣示您的轩然大波,并且将你的委托用作类型

除此而外行使自定义委托项目来定义事件外,还足以使用.NET类库中预约义的信托项目伊芙ntHandler来定义事件,必要小心它们的参数。

泛型参数的事件,实例:

   }

class Publisher
{
    public event CustomEventHandler RaiseCustomEvent;
}

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

      • 假使选择的是泛型版本,则不须求自定义委托。相反,应将事件类型钦点为
        伊夫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(“李四”);
金沙注册送58 ,    Friend friend3=new Friend(“王五”);

   }

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

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

}

     在开立可用作别的类的基类的类时,必须思考如下事实:事件是杰出类其他委托,只好够从注明它们的类中调用。派生类不恐怕直接调用基类中扬言的轩然大波。即使有时你可能希望某些事件只可以通过基类引发,但在当先二分之1景色下,您应该允许派生类调用基类事件。为此,您能够在富含该事件的基类中创设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();

     上边包车型客车言传身教演示怎样处理以下的不常见动静:您的类是从八个以上的接口继承的,各类接口都饱含同名事件)。在那种场馆下,您至少要为在那之中三个风浪提供显式接口达成。为事件编写显式接口达成时,必须编写制定add 和 remove
事件访问器。那七个事件访问器经常由编写翻译器提供,但在那种意况下编写翻译器不能够提供。

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

   }

     您能够提供本人的访问器,以便内定那四个事件是由你的类中的同一事件代表,依然由差别事件代表。例如,依据接口规范,若是事件应在不一致时间引发,则能够将各样事件与类中的三个独自达成关系。在底下的以身作则中,订户将造型引用强制转换为
IShape 或 IDrawingObject,从而显明本人将会收取哪个 OnDraw 事件。

从概念中得以看来,该委托类型的归来类型为void,第贰个参数sender负责保存触发事件目的的引用,其连串为object;首个参数e负责保存事件数量。伊夫ntArgs类也是.NET类库中定义的类,它不保留任何数据,假若想在事变中隐含事件数量,就务须选取伊芙ntArgs的派生类来落成。

}

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类

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

 采纳字典存款和储蓄事件实例                                       

下面说了,假设要在事变中带有事件数量,就务须利用伊芙ntArgs的派生类。具体的贯彻代码如下:

Action<T>泛型委托封装一个主意,该格局只使用叁个参数并且不重返值。

     accessor-declarations
的一种用法是公开大气的事件但不为每一种事件分配字段,而是采纳字典来存储那么些事件实例。那只有在享有至极多的事件、但您测度半数以上事变都不会落到实处时才有用。

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(“————————————“);
    //使用”-=”来撤销事件订阅,此时李4将收不到公告
    bridegroom.MarryEvent-=new MarryHandler(friend2.SendMessage);
    bridegroom.MarryEvent+=new MarryHandler(friend3.SendMessage);
    bridegroom.OnMarriageComing(“Friend,I Will Marry!!”);
    Console.ReadKey();
  }
}

using System;

     有各类主意可向客户端代码公开异步成效。基于事件的异步格局为类规定了用于展现异步行为的提出措施。对于相对简便易行的10二线程应用程序,BackgroundWorker
组件提供了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

金沙注册送58 2

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

图1

      messageTarget(“Hello, World!”);

 

   }

金沙注册送58 3

}

图2

上边包车型地铁以身作则演示怎样使用 Action(T) 委托来打字与印刷 List(T) 对象的内容。在此示例中,使用 Print 方法将列表的情节展现到控制台上。别的,C# 示例还演示怎样使用匿名格局将内容体现到控制台上。

 

using System;

金沙注册送58 4

using System.Collections.Generic;

图3

class Program

金沙注册送58 5

{

图4

    static void Main()

能够看出,C#事件被编写翻译成包罗三个公共措施的代码段,二个饱含add_前缀,另1个富含remove_前缀,前缀后边是C#事件的名目。

    {

在add_艺术中,通过调用了Delegate.Combine()方法来贯彻的(图三中红框的地方),Delegate.Combine()方法将多少个委托组合为了一个多路广播委托。

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

在remove_艺术中,同样选拔了Delegate.Remove()方法。

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

由地点的四张图中能够总计出:

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

C#的风云是二个奇异的多路广播委托,事件暗许含有一个个体的委托项目变量(图贰的红框),该变量用于保存对事件处理方法的引用,且该信托项目标变量为私有,只可以从概念该事件的类中举办访问。

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

    }

}

叁.       有叁个输入参数再次回到值为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);

   }

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

Action<T1,T2,T叁>委托,封装三个办法,该方法应用八个参数并且不重临值。

能够采纳 Action(T1, T2,
T3
) 委托以参数格局传递形式,而不用显式证明自定义的寄托。

该措施必须与此委托定义的措施签名相对应。也正是说,封装的艺术必须具备八个均经过值传递给它的参数,并且无法重临值。

伍.       有6个输入参数重回值为void的信托

Action<T一,T2,T3,T4>委托, 封装三个方法,该措施具有七个参数并且不再次来到值。

可以行使 Action(T1, T2, T3,
T4
) 委托以参数格局传递方式,而不用显式注明自定义的信托。封装的诀要必须与此委托定义的章程签名相呼应。约等于说,封装的章程必须有所八个均经过值传递给它的参数,并且无法再次来到值。

二.Func系统的泛型委托

Func种类的嘱托定义的是重临值的嘱托。它有多少个版本包含未有输入参数,二个输入参数,三个输入参数,一个输入参数,多少个输入参数共八个本子那多少个本子的原型如下:

一.       没有输入参数有再次回到值(再次回到值不为void)的嘱托

Func<TResult>封装3个不拥有参数但却回到 TResult 参数钦点的类型值的诀窍。
能够行使此委托构造二个能以参数方式传递的格局,而不用显式评释自定义的寄托。该

艺术必须与此委托定义的主意签名相对应。那表示封装的诀窍不得持有参数,但无法不重回值。

2.       具有三个输入参数有重临值(重回值不为void)的嘱托

  
Func<T,TResult>封装贰个装有一个参数并重临 TResult 参数钦赐的类型值的主意。

能够利用此委托构造三个能以参数格局传递的不2诀窍,而不用显式注解自定义的委托。该办法必须与此委托定义的方法签名相呼应。也便是说,封装的法子必须具有二个由此值传递给它的参数,并且必须重回值。

叁.       具有3个输入参数有重临值(重回值不为void)的嘱托

  Func<T①,T二,TResult>封装一个拥有3个参数并赶回 TResult 参数内定的类型值的法子。

能够动用此委托构造一个能以参数方式传递的措施,而不用显式注脚自定义的委托。该办法必须与此委托定义的不二诀要签名相呼应。也正是说,封装的方式必须持有五个均经过值传递给它的参数,并且必须再次回到值

四.       具有七个输入参数有再次来到值(重回值不为void)的嘱托

  
Func<T一,T二,T三,TResut>封装五个负有三个参数并赶回 TResult 参数钦定的类型值的方法。

能够选择此委托构造一个能以参数方式传递的章程,而不用显式表明自定义的嘱托。该办法必须与此委托定义的艺术签名相呼应。也正是说,封装的不2法门必须拥有多个均通过值传递给它的参数,并且必须重回值。

5.       具有两个输入参数有重临值(再次来到值不为void)的信托

 Func<T一,T二,T三,TResult>封装三个有着多个参数并回到 TResult 参数内定的类型值的方法。

能够应用此委托构造2个能以参数方式传递的章程,而不用显式注明自定义的寄托。该方式必须与此委托定义的艺术签名相呼应。也等于说,封装的不二等秘书诀必须持有多个均经过值传递给它的参数,并且必须重临值。

三. EventHandler(TEventArgs) 泛型委托

EventHandler(TEventArgs) 是一种预订义委托,表示事件的事件处理程序方法,它与事件是或不是变动事件数量非亲非故。借使事件不成形事件数量,则用EventArgs 替代泛型类型参数;不然,提供自个儿的自定义事件数据类型并用该类型替代泛型类型参数。

利用 伊芙ntHandler<(Of
<(T伊芙ntArgs>)>) 的帮助和益处在于,如若事件生成事件数量,则无需编写自身的自定义委托代码。其余,.NET
Framework 只需贰个贯彻就能帮衬 伊芙ntHandler<(Of
<(T伊夫ntArgs>)>),那与代表泛型类型参数的事件数据类型非亲非故。

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

咱俩精晓,.NET
Framework 中的事件模型基于已有事件委托,该信托将事件与事件处理程序连接。引发轩然大波需求多个成分:

1     引用向事件提供响应的形式的信托。

二保存事件数量的类。

下边是MSDN上的三个简练的事例:

上面包车型大巴代码示例表明事件数量和行使该事件数量的 伊芙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));

    }

}

一旦在从前的老方法则上边的类则应该写为:

//首先得定义一个信托

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

    }

}

4.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地图