方法 : 派生クラスから基本クラス イベントを発生させる (C# プログラミング ガイド)
ここでは、単純な例を使用して、基本クラスで宣言したイベントを派生クラスから発生させる標準的な方法を説明します。このパターンは、.NET Framework クラス ライブラリの Windows フォーム クラスで広く使用されています。
他のクラスの基本クラスとして使用できるクラスを作成するときは、イベントは宣言元のクラスからしか呼び出せない特別なデリゲートであることを考慮する必要があります。派生クラスは、基本クラスの中で宣言されたイベントを直接呼び出せません。常に基本クラスからイベントを発生させるようにしたい場合もありますが、ほとんどの場合、派生クラスから基本クラス イベントを発生させることができるようにします。このためには、イベントをラップする基本クラス内に保護された呼び出しメソッドを作成します。この起動メソッドを呼び出すかオーバーライドすることによって、派生クラスから間接的にイベントを呼び出せます。
[!メモ]
基本クラスで仮想イベントを宣言したり、派生クラスでそれらをオーバーライドしたりしないでください。C# コンパイラはこれらを正しく処理できない派生し、イベントに対するサブスクライバーが基本クラスのイベントを実際にサブ スクライブするかどうかは予測できません。
使用例
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 * radius;
}
public void Update(double d)
{
radius = d;
area = 3.14 * radius * 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 10201.86
Drawing a circle
Received event. Shape area is now 49
Drawing a rectangle
*/