结合.net框架在C#派生类中触发基类事件及实现接口事件

作者:goldensun 时间:2022-02-23 20:39:57 

在派生类中引发基类事件
以下简单示例演示了在基类中声明可从派生类引发的事件的标准方法。此模式广泛应用于 .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();
   }
 }
}

输出:


   Received event. Shape area is now 10201.86
   Drawing a circle
   Received event. Shape area is now 49
   Drawing a rectangle


实现接口事件

接口可声明事件。下面的示例演示如何在类中实现接口事件。实现接口事件的规则与实现任何接口方法或属性的规则基本相同。
在类中实现接口事件
在类中声明事件,然后在适当的区域调用该事件。


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)
   {
     if(ShapeChanged != null)
     {
       ShapeChanged(this, e);
     }
   }
 }

}

下面的示例演示如何处理以下的不常见情况:您的类是从两个以上的接口继承的,每个接口都含有同名事件)。在这种情况下,您至少要为其中一个事件提供显式接口实现。为事件编写显式接口实现时,必须编写 add 和 remove 事件访问器。这两个事件访问器通常由编译器提供,但在这种情况下编译器不能提供。
您可以提供自己的访问器,以便指定这两个事件是由您的类中的同一事件表示,还是由不同事件表示。例如,根据接口规范,如果事件应在不同时间引发,则可以将每个事件与类中的一个单独实现关联。在下面的示例中,订户将形状引用强制转换为 IShape 或 IDrawingObject,从而确定自己将会接收哪个 OnDraw 事件。


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
   event EventHandler IDrawingObject.OnDraw
   {
     add
     {
       lock (objectLock)
       {
         PreDrawEvent += value;
       }
     }
     remove
     {
       lock (objectLock)
       {
         PreDrawEvent -= value;
       }
     }
   }
   // 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.
     EventHandler handler = PreDrawEvent;
     if (handler != null)
     {
       handler(this, new EventArgs());
     }
     Console.WriteLine("Drawing a shape.");

// RaiseIShape's event after the object is drawn.
     handler = PostDrawEvent;
     if (handler != null)
     {
       handler(this, new EventArgs());
     }
   }
 }
 public class Subscriber1
 {
   // References the shape object as an IDrawingObject
   public Subscriber1(Shape shape)
   {
     IDrawingObject d = (IDrawingObject)shape;
     d.OnDraw += new EventHandler(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 += new EventHandler(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();
   }
 }

}

输出:


 Sub1 receives the IDrawingObject event.
 Drawing a shape.
 Sub2 receives the IShape event.
标签:C#,派生类,事件,接口
0
投稿

猜你喜欢

  • 关于Spring Data Jpa 自定义方法实现问题

    2023-11-28 10:08:32
  • 用c# 自动更新程序

    2023-04-03 09:03:46
  • 利用Spring Boot和JPA创建GraphQL API

    2023-04-01 07:11:41
  • JavaBean和SpringBean的区别及创建SpringBean方式

    2022-05-23 03:32:14
  • 浅谈Java中ArrayList线程不安全怎么办

    2023-10-02 19:24:56
  • WPF ProgressBar实现实时进度效果

    2023-01-14 08:05:49
  • Java Comparable和Comparator对比详解

    2022-08-13 01:28:08
  • Java使用Freemarker页面静态化生成的实现

    2022-07-24 08:48:42
  • list集合去除重复对象的实现

    2022-10-16 23:02:42
  • C#远程获取图片文件流的方法

    2023-03-17 15:15:18
  • springboot整合quartz项目使用案例

    2023-02-13 19:57:12
  • C#解析json字符串总是多出双引号的原因分析及解决办法

    2022-10-22 02:40:46
  • Android开发之删除项目缓存的方法

    2021-07-22 17:15:45
  • C#简单了解接口(Interface)使用方法

    2022-01-30 18:33:57
  • Android 6.0动态权限申请教程

    2023-09-26 16:43:56
  • C#模板方法模式(Template Method Pattern)实例教程

    2022-09-12 02:59:23
  • 如何从dump文件中提取出C#源代码

    2022-09-13 19:54:54
  • 一文详解如何在控制台显示MyBatis的SQL语句

    2023-01-09 06:43:38
  • 详解JVM之运行时常量池

    2022-08-04 03:41:03
  • 可视化Swing中JTable控件绑定SQL数据源的两种方法深入解析

    2023-11-28 19:13:58
  • asp之家 软件编程 m.aspxhome.com