.NET 事件

时间:2019-09-18 20:15来源:盲区行者
     在创制可用作任何类的基类的类时,必得思虑如下事实:事件是独特类型的嘱托,只好够从注解它们的类中调用。派生类不可能间接调用基类中宣称的事件。尽管临时你或者希望

     在创制可用作任何类的基类的类时,必得思虑如下事实:事件是独特类型的嘱托,只好够从注解它们的类中调用。派生类不可能间接调用基类中宣称的事件。尽管临时你或者希望有个别事件只好通过基类引发,但在当先四分之二景色下,您应该允许派生类调用基类事件。为此,您能够在包含该事件的基类中创造二个受保证的调用方法。通过调用或重写此调用方法,派生类便能够直接调用该事件。

     假诺你想编写引发风云时调用的自定义代码,则足以订阅由另外类宣布的事件。譬喻,能够订阅有些开关的“单击”事件,以使应用程序在客商单击该按键时实行一些平价的操作。

 事件的订阅和注销订阅                                       

publisher.RaiseCustomEvent -= HandleCustomEvent;
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();
        }
    }
}
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");
    }
}

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

 事件概述                                                           

     在类中宣称事件,然后在方便的职分调用该事件。

public delegate void EventHandler(object sender, EventArgs e);
public delegate void CustomEventHandler(object sender, CustomEventArgs a);
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();
        }
    }
}

     上面包车型客车示范演示咋样管理以下的不常见景况:您的类是从七个以上的接口承接的,种种接口都含有同名事件)。在这种景观下,您至少要为当中三个事件提供显式接口完结。为事件编写显式接口达成时,必得编写制定add 和 remove 事件访谈器。那五个事件访谈器平常由编写翻译器提供,但在这种状态下编写翻译器不可能提供。

void HandleCustomEvent(object sender, CustomEventArgs a){  }

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

     您能够提供温馨的访谈器,以便钦点那多个事件是由你的类中的同一事件代表,依旧由分化事件表示。举个例子,依照接口标准,尽管事件应在不一样一时候间引发,则能够将各种事件与类中的三个独立完结关系。在底下的身体力行中,订户将造型援用强制转换为 IShape 或 IDrawingObject,进而分明自个儿将会接到哪个 OnDraw 事件。

     接口可表明事件。上面包车型客车亲自过问演示怎么样在类中落到实处接口事件。接口事件的贯彻准则与另外接口方法或质量的落到实处法则基本同样。

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

     在爆发其余类或对象关心的事情时,类或对象可经过事件通报它们。发送(或吸引)事件的类称为“发行者”,接收(或管理)事件的类称为“订户”。

.NET 事件。     以下简单示例演示了在基类中声称可从派生类引发的平地风波的行业内部方法。此格局布满应用于 .NET Framework 基类库中的 Windows 窗体类。

      • .NET 事件。假若应用的是 EventHandler 的非泛型版本,况且您有一个由 EventArgs 派生的自定义类,请在发布类中宣示您的事件,并且将你的委托用作类型
    • 匿有名的模特式订阅事件
      • 选择加法赋值运算符 (+=) 来为事件附加佚名方式。在下边包车型大巴示范中,假如名称为 publisher 的目的具有三个名叫 RaiseCustom伊芙nt 的平地风波,并且还定义了多少个CustomEventArgs 类以承载有些品种的专项使用事件消息。请留神,订户类必要引用publisher 才具订阅其事件。
  • .NET 事件。运用 EventHandler 方式公布事件
    • (如若无需发送含事件的自定义数据,请跳过此步骤,直接进入步骤 3。)在发行者类和订户类均可看见的限量中评释类,并充分保留自定义事件数量所需的分子。在此示例中,会回来三个简易字符串。
  • 特点
    • 发行者分明何时引发事件,订户显著实践何种操作来响应该事件。
    • 多少个事件能够有八个订户。二个订户可管理来自几个发行者的多个事件。
    • 从没订户的平地风波永世不会被调用。
    • 事件司空眼惯用于通告顾客操作
    • 一经二个平地风波有多个订户,当引发该事件时,会联合调用三个事件管理程序,也得以设置异步调用事件。
    • 能够运用事件联合线程。
    • 事件是依照 伊夫ntHandler 委托和 伊芙ntArgs 基类的。

 抓住派生类中的基类事件                                      

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

     有种种措施可向顾客端代码公开异步功用。基于事件的异步格局为类规定了用来体现异步行为的建议措施。对于相对简单的多线程应用程序,BackgroundWorker 组件提供了一个简约的化解方案。对于更复杂的异步应用程序,请思考完成一个合乎基于事件的异步方式的类。

      • 假定利用的是泛型版本,则无需自定义委托。相反,应将事件类型内定为 伊芙ntHandler<Custom伊芙ntArgs>,在尖括号内放置您自个儿的类的称谓。
class Publisher
{
    public event CustomEventHandler RaiseCustomEvent;
}
    • 使用以下任一步骤,在发表类中声称事件。
      • 若无自定义 EventArgs 类,事件类型正是非泛型 EventHandler 委托。它不必要注脚,因为它已在 C# 项目私下认可富含的 System 命名空间中张开了注脚
  • 在类中贯彻接口事件

     accessor-declarations 的一种用法是芸芸众生大气的事件但不为每一种事件分配字段,而是利用字典来囤积那些事件实例。这唯有在颇具非常多的事件、但您推断半数以上平地风波都不会达成时才有用。

 宣布规范事件                                           

 兑现接口事件                                            

    • “在后台”实行耗费时间任务(比方下载和数据库操作),但不会停顿您的应用程序。
    • 而且实践多少个操作,每一个操作完毕时都会吸收通报。
    • 等待能源变得可用,但不会结束(“挂起”)您的应用程序。
    • 采纳深谙的事件和寄托模型与挂起的异步操作通讯。
  • 注销订阅
      • 应用加法赋值运算符 (+=) 来为事件附加事件管理程序。在底下的演示中,假使名叫 publisher 的靶子具备一个名叫 RaiseCustomEvent 的事件。请留神,订户类须求援引发行者类本事订阅其事件。
publisher.RaiseCustomEvent += HandleCustomEvent;
publisher.RaiseCustomEvent += new CustomEventHandler(HandleCustomEvent);

     要防止在吸引平地风波时调用事件管理程序,您只需撤消订阅该事件。要严防能源败露,请在放出订户对象在此之前撤废订阅事件,那点非常重大。在裁撤订阅事件从前,在发布对象中作为该事件的基础的多路广播委托会援用封装了订户的事件管理程序的信托。只要发表对象满含该援引,就不会对订户对象实行垃圾回收。

 事件的异步形式                            

     上面包车型地铁进程演示了怎样将符合标准 .NET Framework 形式的风浪增加到您自身的类和布局中。.NET Framework 类库中的全体事件均基于 伊夫ntHandler 委托,定义如下。

public event EventHandler RaiseCustomEvent;
  • 订阅事件
    • VS IDE 订阅事件
      • 一经“属性”窗口不可见,请在“设计”视图中,右击要创设事件管理程序的窗体或控件,然后接纳“属性”。
      • 在“属性”窗口的顶端,单击“事件”Logo。
      • 双击要开创的事件,Visual C# 会创立贰个空事件管理程序方法,并将其增添到您的代码中。恐怕,您也足以在“代码”视图中手动增添代码。
    • 编制程序方式订阅事件

      • 概念一个事件管理程序方法,其签字与该事件的嘱托具名匹配。举个例子,假若事件基于 伊夫ntHandler 委托类型,则下边包车型大巴代码表示方法存根
public class CustomEventArgs : EventArgs
{
    public CustomEventArgs(string s)
    {
        msg = s;
    }
    private string msg;
    public string Message
    {
        get { return msg; }
    } 
}
    • (假诺您使用的是 EventHandler 的泛型版本,请跳过此步骤。)在颁发类中声称一个寄托。为它钦赐以 伊夫ntHandler 结尾的称谓。第贰个参数钦命自定义 伊芙ntArgs 类型。

编辑:盲区行者 本文来源:.NET 事件

关键词: