共用方式為


HOW TO:在衍生類別中引發基底類別事件 (C# 程式設計手冊)

更新:2007 年 11 月

下列的簡單範例會示範在基底類別 (Base Class) 內宣告事件的標準方式,讓事件也可以從衍生類別 (Derived Class) 中引發。這個模式會在 .NET Framework 類別庫 (Class Library) 的 Windows Form 類別中廣泛使用。

當您建立可當做其他類別之基底類別的類別時,您應該考量一項事實:事件是一種委派 (Delegate) 的特殊型別,只能從宣告事件的類別中予以叫用 (Invoke)。衍生類別無法直接叫用在基底類別中宣告的事件。雖然有時您可能會想要使用只能由基底類別所引發的事件,但是在多數情形下,您應該啟用衍生類別來叫用基底類別事件。若要這樣做,您可以在包裝事件的基底類別內建立保護的 (Protected) 的叫用方法;藉由呼叫或覆寫這個叫用方法,衍生類別便能夠間接地叫用該事件。

注意事項:

請勿在基底類別中宣告虛擬事件,以及進而在衍生類別中覆寫它們。因為在 Microsoft Visual Studio 2008 中的 C# 編譯器 (Compiler) 不會正確地處理這些事件,而且此時無法預測衍生事件的訂閱者是否確實會訂閱該基底類別事件。

範例

namespace BaseClassEvents
{
    using System;
    using System.Collections.Generic;

    // Special EventArgs class to hold info about Shapes.
    public class ShapeEventArgs : EventArgs
    {
        private double newArea;

        public ShapeEventArgs(double d)
        {
            newArea = d;
        }
        public double NewArea
        {
            get { return newArea; }
        }
    }

    // Base class event publisher
    public abstract class Shape
    {
        protected double area;

        public double Area
        {
            get { return area; }
            set { area = value; }
        }
        // The event. Note that by using the generic EventHandler<T> event type
        // we do not need to declare a separate delegate type.
        public event EventHandler<ShapeEventArgs> ShapeChanged;

        public abstract void Draw();

        //The event-invoking method that derived classes can override.
        protected virtual void OnShapeChanged(ShapeEventArgs e)
        {
            // Make a temporary copy of the event to avoid possibility of
            // a race condition if the last subscriber unsubscribes
            // immediately after the null check and before the event is raised.
            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)
        {
            // Do any circle-specific processing here.

            // Call the base class event invocation method.
            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)
        {
            // Do any rectangle-specific processing here.

            // Call the base class event invocation method.
            base.OnShapeChanged(e);
        }
        public override void Draw()
        {
            Console.WriteLine("Drawing a rectangle");
        }

    }

    // Represents the surface on which the shapes are drawn
    // Subscribes to shape events so that it knows
    // when to redraw a shape.
    public class ShapeContainer
    {
        List<Shape> _list;

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

        public void AddShape(Shape s)
        {
            _list.Add(s);
            // Subscribe to the base class event.
            s.ShapeChanged += HandleShapeChanged;
        }

        // ...Other methods to draw, resize, etc.

        private void HandleShapeChanged(object sender, ShapeEventArgs e)
        {
            Shape s = (Shape)sender;

            // Diagnostic message for demonstration purposes.
            Console.WriteLine("Received event. Shape area is now {0}", e.NewArea);

            // Redraw the shape here.
            s.Draw();
        }
    }

    class Test
    {

        static void Main(string[] args)
        {
            //Create the event publishers and subscriber
            Circle c1 = new Circle(54);
            Rectangle r1 = new Rectangle(12, 9);
            ShapeContainer sc = new ShapeContainer();

            // Add the shapes to the container.
            sc.AddShape(c1);
            sc.AddShape(r1);

            // Cause some events to be raised.
            c1.Update(57);
            r1.Update(7, 7);

            // Keep the console window open in debug mode.
            System.Console.WriteLine("Press any key to exit.");
            System.Console.ReadKey();
        }
    }
}
/* Output:
        Received event. Shape area is now 178.98
        Drawing a circle
        Received event. Shape area is now 49
        Drawing a rectangle
 */

請參閱

概念

C# 程式設計手冊

參考

事件 (C# 程式設計手冊)

委派 (C# 程式設計手冊)

存取修飾詞 (C# 程式設計手冊)

其他資源

在 Windows Form 中建立事件處理常式