如何:实现接口事件(C# 编程指南)How to: Implement Interface Events (C# Programming Guide)

接口可以声明事件An interface can declare an event. 下面的示例演示如何在类中实现接口事件。The following example shows how to implement interface events in a class. 这些规则基本上都与实现任何接口方法或属性时的相同。Basically the rules are the same as when you implement any interface method or property.

在类中实现接口事件To implement interface events in a class

在类中声明事件,然后在相应区域中调用它。Declare the event in your class and then invoke it in the appropriate areas.

namespace ImplementInterfaceEvents  
{  
    public interface IDrawingObject  
    {  
        event EventHandler ShapeChanged;  
    }  
    public class MyEventArgs : EventArgs   
    {  
        // class members  
    }  
    public class Shape : IDrawingObject  
    {  
        public event EventHandler ShapeChanged;  
        void ChangeShape()  
        {  
            // Do something here before the event…  

            OnShapeChanged(new MyEventArgs(/*arguments*/));  

            // or do something here after the event.   
        }  
        protected virtual void OnShapeChanged(MyEventArgs e)  
        {  
            ShapeChanged?.Invoke(this, e);  
        }  
    }  

}  

示例Example

下面的示例演示如何处理不太常见的情况:类继承自两个或多个接口,且每个接口都具有相同名称的事件。The following example shows how to handle the less-common situation in which your class inherits from two or more interfaces and each interface has an event with the same name. 在这种情况下,你必须为至少其中一个事件提供显式接口实现。In this situation, you must provide an explicit interface implementation for at least one of the events. 为事件编写显式接口实现时,还必须编写 addremove 事件访问器。When you write an explicit interface implementation for an event, you must also write the add and remove event accessors. 通常这些访问器由编译器提供,但在这种情况下编译器不提供它们。Normally these are provided by the compiler, but in this case the compiler cannot provide them.

通过提供自己的访问器,可以指定两个事件是由类中的同一个事件表示,还是由不同事件表示。By providing your own accessors, you can specify whether the two events are represented by the same event in your class, or by different events. 例如,如果根据接口规范应在不同时间引发事件,可以在类中将每个事件与单独实现关联。For example, if the events should be raised at different times according to the interface specifications, you can associate each event with a separate implementation in your class. 在下面的示例中,订阅服务器确定它们通过将形状引用转换为 IShapeIDrawingObject 接收哪个 OnDraw 事件。In the following example, subscribers determine which OnDraw event they will receive by casting the shape reference to either an IShape or an IDrawingObject.

namespace WrapTwoInterfaceEvents
{
    using System;

    public interface IDrawingObject
    {
        // Raise this event before drawing
        // the object.
        event EventHandler OnDraw;
    }
    public interface IShape
    {
        // Raise this event after drawing
        // the shape.
        event EventHandler OnDraw;
    }


    // Base class event publisher inherits two
    // interfaces, each with an OnDraw event
    public class Shape : IDrawingObject, IShape
    {
        // Create an event for each interface event
        event EventHandler PreDrawEvent;
        event EventHandler PostDrawEvent;

        object objectLock = new Object();

        // Explicit interface implementation required.
        // Associate IDrawingObject's event with
        // PreDrawEvent
        #region IDrawingObjectOnDraw
        event EventHandler IDrawingObject.OnDraw
        {
            add
            {
                lock (objectLock)
                {
                    PreDrawEvent += value;
                }
            }
            remove
            {
                lock (objectLock)
                {
                    PreDrawEvent -= value;
                }
            }
        }
        #endregion
        // Explicit interface implementation required.
        // Associate IShape's event with
        // PostDrawEvent
        event EventHandler IShape.OnDraw
        {
            add
            {
                lock (objectLock)
                {
                    PostDrawEvent += value;
                }
            }
            remove
            {
                lock (objectLock)
                {
                    PostDrawEvent -= value;
                }
            }


        }

        // For the sake of simplicity this one method
        // implements both interfaces. 
        public void Draw()
        {
            // Raise IDrawingObject's event before the object is drawn.
            PreDrawEvent?.Invoke(this, EventArgs.Empty);

            Console.WriteLine("Drawing a shape.");

            // Raise IShape's event after the object is drawn.
            PostDrawEvent?.Invoke(this, EventArgs.Empty);
        }
    }
    public class Subscriber1
    {
        // References the shape object as an IDrawingObject
        public Subscriber1(Shape shape)
        {
            IDrawingObject d = (IDrawingObject)shape;
            d.OnDraw += d_OnDraw;
        }

        void d_OnDraw(object sender, EventArgs e)
        {
            Console.WriteLine("Sub1 receives the IDrawingObject event.");
        }
    }
    // References the shape object as an IShape
    public class Subscriber2
    {
        public Subscriber2(Shape shape)
        {
            IShape d = (IShape)shape;
            d.OnDraw += 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();

            // Keep the console window open in debug mode.
            System.Console.WriteLine("Press any key to exit.");
            System.Console.ReadKey();
        }
    }

}
/* Output:
    Sub1 receives the IDrawingObject event.
    Drawing a shape.
    Sub2 receives the IShape event.
*/

请参阅See also