【亚洲必赢官网】params关键字修饰的参数的匿名委托和拉姆da表达式,事件的解密

委托

原为出处:

 事件概述                                                           

在日前壹篇中写到了寄托,也说了委托是C#中众多特色的根底,那篇要讲的风云,便是起家在委托之上的。在C#一.0中,委托和事件是最珍视的多少个特点。

寄托与事件,日常拿来就用,往往忘记其落成原理,对其使用办法也更为局限。周家安先生在《C#
陆.0
学习笔记》中对信托和事件的教学,深切浅出,清晰明了,故专程摘抄一篇小说,勤看勤思。

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

   
 在发出其余类或对象关切的事务时,类或对象可由此事件通报它们。发送(或吸引)事件的类称为“发行者”,接收(或处理)事件的类称为“订户”。

一、什么是事件?

首先,委托是1种样式上与格局签名相似的类型。

1.Action多级的泛型委托

  • 特点
    • 发行者显然何时引发事件,订户明确实施何种操作来响应该事件。
    • 1个风云能够有两个订户。二个订户可处理来自多少个发行者的四个事件。
    • 向来不订户的风云永远不会被调用。
    • 事件数见不鲜用于布告用户操作
    • 若果1个事件有七个订户,当引发该事件时,会同步调用四个事件处理程序,也能够安装异步调用事件。
    • 能够行使事件联合线程。
    • 事件是依照 伊芙ntHandler 委托和
      伊夫ntArgs 基类的。

事件设计到两类角色——事件公布者和事件订阅者。当有个别事件时有产生后,事件公布者会宣布新闻;事件订阅者会收取到音讯,并做出相应的拍卖,那正是事件的进度。

概念3个信托:

Action连串的嘱托定义的是未曾重临值(重临值为void)的信托。它有八个版本包罗未有输入参数,2个输入参数,三个输入参数,3个输入参数,几个输入参数共七个本子那多少个本子的原型如下:

 事件的订阅和收回订阅                                       

 

public delegate void DoSome(string msg);

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

     固然你想编写引发轩然大波时调用的自定义代码,则足以订阅由别的类发表的事件。例如,能够订阅某些按钮的“单击”事件,以使应用程序在用户单击该按钮时实施一些实用的操作。

2、使用事件

 

Action委托 封装2个艺术,该办法不选拔参数并且不重返值。

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

      • 概念二个事件处理程序方法,其签名与该事件的嘱托签名相配。例如,假使事件基于
        伊芙ntHandler 委托类型,则上面包车型地铁代码表示方法存根

贰.一 定义事件

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

能够选拔此委托以参数情势传递2个实施某操作的方法,而不用显式声惠氏(WYETH)个自定义的嘱托来封装此方法。该包装的办法必须与此委托定义的办法签名相对应。这意味着该方式不得持有参数和再次回到值。例:

void HandleCustomEvent(object sender, CustomEventArgs a){  }

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

 

using System;

      • 使用加法赋值运算符 (+=)
        来为事件附加事件处理程序。在下边包车型客车言传身教中,若是名称叫 publisher
        的目的具备八个名称为 RaiseCustom伊芙nt
        的风云。请小心,订户类供给引用发行者类才能订阅其事件。

public event EventHandler birthday;

 

using System.Windows.Forms;

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

内部event是任重(Ren Zhong)而道远字,而伊夫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);
};

贰.二 订阅和打消事件

 委托列表

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

亚洲必赢官网 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
    情势发表事件
    • (假使不须求发送含事件的自定义数据,请跳过此步骤,直接进去步骤
      三。)在发行者类和订户类均可尽收眼底的限定中评释类,并丰盛保留自定义事件数量所需的分子。在此示例中,会回来三个简便字符串。

    //使用”-=”来撤消事件订阅,此时李肆将收不到公告
    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
      的泛型版本,请跳过此步骤。)在发布类中扬言三个寄托。为它内定以
      EventHandler 结尾的称号。第一个参数钦命自定义 伊芙ntArgs
      类型。

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

 引发风云的靶子实例将传递给sender参数,而与事件有关的数码则传递给e参数。若是不须求传递过多的数额,能够透过System.伊芙ntArgs.Empty静态成员再次回到1个空的伊芙ntArgs对象类传递。

   }

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

    Console.ReadKey();
  }
}

不过,由于不一样的轩然大波要传送的参数不一致,越来越多时候是从EventArgs类派生的子类的实例,分明二个伊芙ntHandler委托是不可能满意种种情状的。如若针对不一致的轩然大波也顶四个三个应和的嘱托,水量一旦多起来,既混乱,也不好管理。为了消除那么些难题,.NET类库又提供了3个涵盖泛型参数的事件处理委托。原型如下:

   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)
    {
【亚洲必赢官网】params关键字修饰的参数的匿名委托和拉姆da表达式,事件的解密。      Console.WriteLine(message);
      Console.WriteLine(this.Name+”收到了,到时候准时插足”);
    }
  }

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

   {

public event EventHandler RaiseCustomEvent;

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

T伊夫ntArgs
是2个泛型参数,应该是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(“李四”);
    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();

     上面包车型客车以身作则演示咋样处理以下的不常见事态:您的类是从七个以上的接口继承的,各种接口都带有同名事件)。在这种情景下,您至少要为个中四个事件提供显式接口完毕。为事件编写显式接口达成时,必须编写制定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类

2.       有3个输入参数重回值为void的信托

 使用字典存款和储蓄事件实例                                       

上边说了,即便要在事变中隐含事件数量,就不能够不运用伊夫ntArgs的派生类。具体的贯彻代码如下:

Action<T>泛型委托封装3个措施,该措施只利用多个参数并且不重回值。

     accessor-declarations
的1种用法是当众大气的事件但不为每一个事件分配字段,而是使用字典来囤积那几个事件实例。那唯有在具备相当多的事件、但你推断大部分轩然大波都不会促成时才有用。

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;

     有各类主意可向客户端代码公开异步功用。基于事件的异步形式为类规定了用来浮现异步行为的建议措施。对于相对简便易行的拾2线程应用程序,BackgroundWorker
组件提供了八个简易的化解方案。对于更复杂的异步应用程序,请思索完成3个适合基于事件的异步情势的类。

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

亚洲必赢官网 2

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

图1

      messageTarget(“Hello, World!”);

 

   }

亚洲必赢官网 3

}

图2

上面包车型客车以身作则演示怎样利用 Action(T) 委托来打字与印刷 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()方法将几个委托组合为了一个多路广播委托。

        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<T壹,T2> 封装2个主意,该措施具有七个参数并且不重临值。

能够应用 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);

   }

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

Action<T1,T2,T三>委托,封装1个措施,该措施应用多个参数并且不重返值。

能够运用 Action(T1, T2,
T3
) 委托以参数情势传递格局,而不用显式评释自定义的嘱托。

该办法必须与此委托定义的措施签名相呼应。也正是说,封装的措施必须有所四个均经过值传递给它的参数,并且不能重回值。

5.       有多少个输入参数重临值为void的寄托

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

能够利用 Action(T1, T2, T3,
T4
) 委托以参数情势传递情势,而不用显式评释自定义的寄托。封装的办法必须与此委托定义的点子签名相呼应。也正是说,封装的主意必须有所多个均经过值传递给它的参数,并且无法重返值。

二.Func系统的泛型委托

Func种类的委托定义的是重回值的嘱托。它有四个本子蕴涵未有输入参数,3个输入参数,二个输入参数,一个输入参数,伍个输入参数共四个本子那个版本的原型如下:

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

Func<TResult>封装多个不富有参数但却再次来到 TResult 参数内定的类型值的办法。
能够利用此委托构造二个能以参数方式传递的艺术,而不用显式注解自定义的委托。该

情势必须与此委托定义的法子签名相呼应。这象征封装的办法不得持有参数,但不可能不重返值。

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

  
Func<T,TResult>封装1个独具二个参数并回到 TResult 参数钦点的类型值的方法。

能够行使此委托构造二个能以参数方式传递的秘籍,而不用显式注解自定义的寄托。该格局必须与此委托定义的措施签名相呼应。也正是说,封装的措施必须有所贰个经过值传递给它的参数,并且必须再次回到值。

叁.       具有二个输入参数有再次来到值(再次回到值不为void)的信托

  Func<T一,T贰,TResult>封装三个怀有三个参数并赶回 TResult 参数钦命的类型值的章程。

能够动用此委托构造三个能以参数格局传递的方法,而不用显式注明自定义的委托。该办法必须与此委托定义的办法签名相呼应。也正是说,封装的点子必须怀有四个均经过值传递给它的参数,并且必须重临值

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

  
Func<T1,T二,T叁,TResut>封装二个装有多少个参数并重返 TResult 参数钦点的类型值的点子。

能够利用此委托构造1个能以参数格局传递的不贰秘诀,而不用显式注明自定义的寄托。该方法必须与此委托定义的格局签名相对应。也正是说,封装的方法必须拥有四个均通过值传递给它的参数,并且必须重临值。

五.       具有多少个输入参数有再次来到值(再次回到值不为void)的寄托

 Func<T一,T二,T三,TResult>封装3个存有四个参数并重返 TResult 参数钦命的类型值的格局。

能够运用此委托构造四个能以参数格局传递的诀要,而不用显式表明自定义的嘱托。该措施必须与此委托定义的章程签名相对应。也正是说,封装的措施必须拥有多个均通过值传递给它的参数,并且必须再次来到值。

三. EventHandler(TEventArgs) 泛型委托

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

利用 伊夫ntHandler<(Of
<(T伊芙ntArgs>)>) 的亮点在于,若是事件生成事件数量,则无需编写自个儿的自定义委托代码。其它,.NET
Framework 只需二个兑现就能支撑 伊芙ntHandler<(Of
<(T伊芙ntArgs>)>),那与代表泛型类型参数的轩然大波数据类型毫无干系。

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

我们清楚,.NET
Framework 中的事件模型基于已有事件委托,该信托将事件与事件处理程序连接。引发轩然大波供给多少个因素:

一     引用向事件提供响应的点子的寄托。

二保留事件数量的类。

上面是MSDN上的叁个差不离的例证:

上边的代码示例注明事件数量和使用该事件数量的 伊夫ntHandler<(Of
<(TEventArgs>)>) 泛型委托,并演示怎样吸引该事件。

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

    }

}

四.params参数类型的信托

在上边介绍的Action和Func体系的嘱托中,不可见利用由params关键字修改的参数,笔者本来想用params关键字来完结可变类型的参数,结果发现行反革命不通,这时只好用转变类型的委托,可是请小心在用匿名格局绑定具有params关键字修饰的参数的信托时,匿名情势的参数中却不能够有params关键字,在匿名格局中唯有去掉了params关键字,尽管从匿名格局中去掉了params关键字,但那并无妨碍行使相同的语法.

比如证明了之类类型的信托

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

唯独你去无法用上面包车型大巴不二秘诀去绑定三个匿名格局:

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

为此,可以将匿名方式绑定到1个选择params申明的委托,这样1来,就能够在调用时传出你期望的任意数指标参数。

网站地图xml地图