Gewusst wie: Auslösen von Basisklassenereignissen in abgeleiteten Klassen (C#-Programmierhandbuch)

Aktualisiert: November 2007

Das folgende einfache Beispiel veranschaulicht die herkömmliche Methode zum Deklarieren von Ereignissen in einer Basisklasse, damit sie über abgeleitete Klassen ausgelöst werden können. Dieses Beispiel wird für zahlreiche Windows Forms-Klassen in der .NET Framework-Klassenbibliothek verwendet.

Wenn Sie eine Klasse erstellen, die als Basisklasse für andere Klassen verwendet werden kann, dürfen Sie nicht vergessen, dass Ereignisse ein spezieller Delegattyp sind und nur innerhalb der Klasse aufgerufen werden können, in der sie deklariert wurden. Abgeleitete Klassen können Ereignisse, die innerhalb der Basisklasse deklariert wurden, nicht direkt aufrufen. Mitunter kann es zwar erwünscht sein, dass ein Ereignis nur von der Basisklasse ausgelöst werden kann, in den meisten Fällen sollten Sie jedoch die abgeleitete Klasse zum Aufrufen von Basisklassenereignissen aktivieren. Hierzu können Sie eine geschützte aufrufende Methode in der Basisklasse erstellen, die das Ereignis umschließt. Abgeleitete Klassen können das Ereignis durch Aufrufen oder Überschreiben dieser aufrufenden Methode indirekt aufrufen.

Hinweis:

Deklarieren Sie keine virtuellen Ereignisse in einer Basisklasse, und überschreiben Sie sie in einer abgeleiteten Klasse. Der C#-Compiler verarbeitet diese Ereignisse in Microsoft Visual Studio 2008 nicht korrekt, und es nicht vorhersehbar, ob ein Abonnent des abgeleiteten Ereignisses tatsächlich das Basisklassenereignis abonniert.

Beispiel

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
 */

Siehe auch

Konzepte

C#-Programmierhandbuch

Referenz

Ereignisse (C#-Programmierhandbuch)

Delegaten (C#-Programmierhandbuch)

Zugriffsmodifizierer (C#-Programmierhandbuch)

Weitere Ressourcen

Erstellen von Ereignishandlern in Windows Forms