文章目录
- 基类事件在派生类中的定义及触发方式
- 基类事件的传播机制
- 示例
- 总结
在面向对象编程中,继承是代码复用的一种重要方式。C#作为一种面向对象的编程语言,允许派生类继承基类的属性和方法。基类定义了一系列共有的属性和行为,而派生类则可以在基类的基础上添加新的特性或重写基类的方法。事件作为一种特殊的成员,也可以在基类中被定义,并在派生类中被触发和使用。
基类事件在派生类中的定义及触发方式
在C#中,事件是一种使类或对象可以通知其他类或对象发生了某些事情的一种机制。在基类中定义事件,然后在派生类中触发这些事件是常见的做法。下面是一个简单的基类事件定义的例子:
public class BaseClass
{
// 定义一个事件
public event EventHandler MyEvent;
// 触发事件的保护方法
protected virtual void OnMyEvent(EventArgs e)
{
MyEvent?.Invoke(this, e);
}
}
在派生类中,我们可以通过调用基类中定义的保护方法来触发事件:
public class DerivedClass : BaseClass
{
// 触发基类事件的派生类方法
public void TriggerEvent()
{
OnMyEvent(EventArgs.Empty);
}
}
基类事件的传播机制
基类事件的传播机制主要有两种:主动传播和自动传播。
主动传播
主动传播是指派生类明确调用基类的事件触发方法。这种方式要求派生类知道基类的事件触发方法,并显式调用它。如上例所示,TriggerEvent 方法调用了 OnMyEvent 方法。
自动传播
自动传播是指派生类重写基类的方法,并在其中触发基类事件。这样,当基类的方法被调用时,事件也会被自动触发。例如:
public class BaseClass
{
public event EventHandler MyEvent;
protected virtual void OnMyEvent(EventArgs e)
{
MyEvent?.Invoke(this, e);
}
public virtual void DoSomething()
{
// 基类方法逻辑
}
}
public class DerivedClass : BaseClass
{
public override void DoSomething()
{
// 派生类自己的逻辑
base.DoSomething(); // 调用基类方法
OnMyEvent(EventArgs.Empty); // 触发事件
}
}
示例
下面是一个完整的示例,展示了基类事件如何在派生类中被触发:
using System;
public class BaseClass
{
public event EventHandler MyEvent;
protected virtual void OnMyEvent(EventArgs e)
{
MyEvent?.Invoke(this, e);
}
public void TriggerBaseEvent()
{
OnMyEvent(EventArgs.Empty);
}
}
public class DerivedClass : BaseClass
{
public void DoSomething()
{
Console.WriteLine("DerivedClass is doing something.");
OnMyEvent(EventArgs.Empty); // 触发基类事件
}
}
class Program
{
static void Main()
{
DerivedClass derived = new DerivedClass();
derived.MyEvent += (sender, e) => Console.WriteLine("Event triggered from DerivedClass.");
derived.DoSomething(); // 触发事件
derived.TriggerBaseEvent(); // 直接触发基类的事件
}
}
在这个示例中,我们创建了一个派生类 DerivedClass,它继承自 BaseClass 并重写了 DoSomething 方法。在这个方法中,我们调用了 OnMyEvent 方法来触发基类的事件。
完整示例
namespace BaseClassEvents
{
// Special EventArgs class to hold info about Shapes.
public class ShapeEventArgs : EventArgs
{
public ShapeEventArgs(double area)
{
NewArea = area;
}
public double NewArea { get; }
}
// Base class event publisher
public abstract class Shape
{
protected double _area;
public double Area
{
get => _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)
{
// Safely raise the event for all subscribers
ShapeChanged?.Invoke(this, e);
}
}
public class Circle : Shape
{
private double _radius;
public Circle(double radius)
{
_radius = radius;
_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)
{
_length = length;
_width = width;
_area = _length * _width;
}
public void Update(double length, double width)
{
_length = length;
_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
{
private readonly List<Shape> _list;
public ShapeContainer()
{
_list = new List<Shape>();
}
public void AddShape(Shape shape)
{
_list.Add(shape);
// Subscribe to the base class event.
shape.ShapeChanged += HandleShapeChanged;
}
// ...Other methods to draw, resize, etc.
private void HandleShapeChanged(object sender, ShapeEventArgs e)
{
if (sender is Shape shape)
{
// Diagnostic message for demonstration purposes.
Console.WriteLine($"Received event. Shape area is now {e.NewArea}");
// Redraw the shape here.
shape.Draw();
}
}
}
class Test
{
static void Main()
{
//Create the event publishers and subscriber
var circle = new Circle(54);
var rectangle = new Rectangle(12, 9);
var container = new ShapeContainer();
// Add the shapes to the container.
container.AddShape(circle);
container.AddShape(rectangle);
// Cause some events to be raised.
circle.Update(57);
rectangle.Update(7, 7);
// Keep the console window open in debug mode.
Console.WriteLine("Press any key to continue...");
Console.ReadKey();
}
}
/* Output:
Received event. Shape area is now 10201.86
Drawing a circle
Received event. Shape area is now 49
Drawing a rectangle
*/
}
总结
在派生类中引发基类事件可以增强代码的可重用性、封装性和灵活性。通过这种方式,我们可以确保基类的行为能够在派生类中得到正确的通知,而无需在每个派生类中重新定义事件。这不仅减少了代码的冗余,还使得基类和派生类之间的交互更加清晰和一致。通过合理地使用事件,我们可以构建出更加健壮和易于维护的面向对象系统。