结合.net框架在C#派生类中触发基类事件及实现接口事件
在派生类中引发基类事件
以下简单示例演示了在基类中声明可从派生类引发的事件的标准方法。此模式广泛应用于.NETFramework类库中的Windows窗体类。
在创建可用作其他类的基类的类时,应考虑如下事实:事件是特殊类型的委托,只可以从声明它们的类中调用。派生类无法直接调用基类中声明的事件。尽管有时需要事件仅由基类引发,但在大多数情形下,应该允许派生类调用基类事件。为此,您可以在包含该事件的基类中创建一个受保护的调用方法。通过调用或重写此调用方法,派生类便可以间接调用该事件。
注意:不要在基类中声明虚拟事件,也不要在派生类中重写这些事件。C#编译器无法正确处理这些事件,并且无法预知的该派生的事件的用户是否真正订阅了基类事件。
namespaceBaseClassEvents
{
usingSystem;
usingSystem.Collections.Generic;
//SpecialEventArgsclasstoholdinfoaboutShapes.
publicclassShapeEventArgs:EventArgs
{
privatedoublenewArea;
publicShapeEventArgs(doubled)
{
newArea=d;
}
publicdoubleNewArea
{
get{returnnewArea;}
}
}
//Baseclasseventpublisher
publicabstractclassShape
{
protecteddoublearea;
publicdoubleArea
{
get{returnarea;}
set{area=value;}
}
//Theevent.NotethatbyusingthegenericEventHandler<T>eventtype
//wedonotneedtodeclareaseparatedelegatetype.
publiceventEventHandler<ShapeEventArgs>ShapeChanged;
publicabstractvoidDraw();
//Theevent-invokingmethodthatderivedclassescanoverride.
protectedvirtualvoidOnShapeChanged(ShapeEventArgse)
{
//Makeatemporarycopyoftheeventtoavoidpossibilityof
//araceconditionifthelastsubscriberunsubscribes
//immediatelyafterthenullcheckandbeforetheeventisraised.
EventHandler<ShapeEventArgs>handler=ShapeChanged;
if(handler!=null)
{
handler(this,e);
}
}
}
publicclassCircle:Shape
{
privatedoubleradius;
publicCircle(doubled)
{
radius=d;
area=3.14*radius*radius;
}
publicvoidUpdate(doubled)
{
radius=d;
area=3.14*radius*radius;
OnShapeChanged(newShapeEventArgs(area));
}
protectedoverridevoidOnShapeChanged(ShapeEventArgse)
{
//Doanycircle-specificprocessinghere.
//Callthebaseclasseventinvocationmethod.
base.OnShapeChanged(e);
}
publicoverridevoidDraw()
{
Console.WriteLine("Drawingacircle");
}
}
publicclassRectangle:Shape
{
privatedoublelength;
privatedoublewidth;
publicRectangle(doublelength,doublewidth)
{
this.length=length;
this.width=width;
area=length*width;
}
publicvoidUpdate(doublelength,doublewidth)
{
this.length=length;
this.width=width;
area=length*width;
OnShapeChanged(newShapeEventArgs(area));
}
protectedoverridevoidOnShapeChanged(ShapeEventArgse)
{
//Doanyrectangle-specificprocessinghere.
//Callthebaseclasseventinvocationmethod.
base.OnShapeChanged(e);
}
publicoverridevoidDraw()
{
Console.WriteLine("Drawingarectangle");
}
}
//Representsthesurfaceonwhichtheshapesaredrawn
//Subscribestoshapeeventssothatitknows
//whentoredrawashape.
publicclassShapeContainer
{
List<Shape>_list;
publicShapeContainer()
{
_list=newList<Shape>();
}
publicvoidAddShape(Shapes)
{
_list.Add(s);
//Subscribetothebaseclassevent.
s.ShapeChanged+=HandleShapeChanged;
}
//...Othermethodstodraw,resize,etc.
privatevoidHandleShapeChanged(objectsender,ShapeEventArgse)
{
Shapes=(Shape)sender;
//Diagnosticmessagefordemonstrationpurposes.
Console.WriteLine("Receivedevent.Shapeareaisnow{0}",e.NewArea);
//Redrawtheshapehere.
s.Draw();
}
}
classTest
{
staticvoidMain(string[]args)
{
//Createtheeventpublishersandsubscriber
Circlec1=newCircle(54);
Rectangler1=newRectangle(12,9);
ShapeContainersc=newShapeContainer();
//Addtheshapestothecontainer.
sc.AddShape(c1);
sc.AddShape(r1);
//Causesomeeventstoberaised.
c1.Update(57);
r1.Update(7,7);
//Keeptheconsolewindowopenindebugmode.
System.Console.WriteLine("Pressanykeytoexit.");
System.Console.ReadKey();
}
}
}
输出:
Receivedevent.Shapeareaisnow10201.86 Drawingacircle Receivedevent.Shapeareaisnow49 Drawingarectangle
实现接口事件
接口可声明事件。下面的示例演示如何在类中实现接口事件。实现接口事件的规则与实现任何接口方法或属性的规则基本相同。
在类中实现接口事件
在类中声明事件,然后在适当的区域调用该事件。
namespaceImplementInterfaceEvents
{
publicinterfaceIDrawingObject
{
eventEventHandlerShapeChanged;
}
publicclassMyEventArgs:EventArgs
{
//classmembers
}
publicclassShape:IDrawingObject
{
publiceventEventHandlerShapeChanged;
voidChangeShape()
{
//Dosomethingherebeforetheevent…
OnShapeChanged(newMyEventArgs(/*arguments*/));
//ordosomethinghereaftertheevent.
}
protectedvirtualvoidOnShapeChanged(MyEventArgse)
{
if(ShapeChanged!=null)
{
ShapeChanged(this,e);
}
}
}
}
下面的示例演示如何处理以下的不常见情况:您的类是从两个以上的接口继承的,每个接口都含有同名事件)。在这种情况下,您至少要为其中一个事件提供显式接口实现。为事件编写显式接口实现时,必须编写add和remove事件访问器。这两个事件访问器通常由编译器提供,但在这种情况下编译器不能提供。
您可以提供自己的访问器,以便指定这两个事件是由您的类中的同一事件表示,还是由不同事件表示。例如,根据接口规范,如果事件应在不同时间引发,则可以将每个事件与类中的一个单独实现关联。在下面的示例中,订户将形状引用强制转换为IShape或IDrawingObject,从而确定自己将会接收哪个OnDraw事件。
namespaceWrapTwoInterfaceEvents
{
usingSystem;
publicinterfaceIDrawingObject
{
//Raisethiseventbeforedrawing
//theobject.
eventEventHandlerOnDraw;
}
publicinterfaceIShape
{
//Raisethiseventafterdrawing
//theshape.
eventEventHandlerOnDraw;
}
//Baseclasseventpublisherinheritstwo
//interfaces,eachwithanOnDrawevent
publicclassShape:IDrawingObject,IShape
{
//Createaneventforeachinterfaceevent
eventEventHandlerPreDrawEvent;
eventEventHandlerPostDrawEvent;
objectobjectLock=newObject();
//Explicitinterfaceimplementationrequired.
//AssociateIDrawingObject'seventwith
//PreDrawEvent
eventEventHandlerIDrawingObject.OnDraw
{
add
{
lock(objectLock)
{
PreDrawEvent+=value;
}
}
remove
{
lock(objectLock)
{
PreDrawEvent-=value;
}
}
}
//Explicitinterfaceimplementationrequired.
//AssociateIShape'seventwith
//PostDrawEvent
eventEventHandlerIShape.OnDraw
{
add
{
lock(objectLock)
{
PostDrawEvent+=value;
}
}
remove
{
lock(objectLock)
{
PostDrawEvent-=value;
}
}
}
//Forthesakeofsimplicitythisonemethod
//implementsbothinterfaces.
publicvoidDraw()
{
//RaiseIDrawingObject'seventbeforetheobjectisdrawn.
EventHandlerhandler=PreDrawEvent;
if(handler!=null)
{
handler(this,newEventArgs());
}
Console.WriteLine("Drawingashape.");
//RaiseIShape'seventaftertheobjectisdrawn.
handler=PostDrawEvent;
if(handler!=null)
{
handler(this,newEventArgs());
}
}
}
publicclassSubscriber1
{
//ReferencestheshapeobjectasanIDrawingObject
publicSubscriber1(Shapeshape)
{
IDrawingObjectd=(IDrawingObject)shape;
d.OnDraw+=newEventHandler(d_OnDraw);
}
voidd_OnDraw(objectsender,EventArgse)
{
Console.WriteLine("Sub1receivestheIDrawingObjectevent.");
}
}
//ReferencestheshapeobjectasanIShape
publicclassSubscriber2
{
publicSubscriber2(Shapeshape)
{
IShaped=(IShape)shape;
d.OnDraw+=newEventHandler(d_OnDraw);
}
voidd_OnDraw(objectsender,EventArgse)
{
Console.WriteLine("Sub2receivestheIShapeevent.");
}
}
publicclassProgram
{
staticvoidMain(string[]args)
{
Shapeshape=newShape();
Subscriber1sub=newSubscriber1(shape);
Subscriber2sub2=newSubscriber2(shape);
shape.Draw();
//Keeptheconsolewindowopenindebugmode.
System.Console.WriteLine("Pressanykeytoexit.");
System.Console.ReadKey();
}
}
}
输出:
Sub1receivestheIDrawingObjectevent. Drawingashape. Sub2receivestheIShapeevent.