虚拟调用与类型检查的另一个示例

问题

我发誓,每当我把它砸到我的脑中,我应该使用虚拟调用和类型检查(例如:

if (obj is Foo) ... else if (obj is Bar) ... 

……我想出了另一个我不知道如何实现前者的例子。

我正在通过串口实现分组化协议。 一些伪代码会解释这个:

 OnDataReceived: RcvPacket p = RcvPacket.ReadPacket(comport); // Call factory method if (p is RcvPacketFoo) OnFoo(); if (p is RcvPacketBar) OnBar(); OnFoo: raise Foo event OnBar: raise Bar event 

基本上,ReadPacket是基类中的工厂方法 ,用于确定接收的数据包类型,并将缓冲区传递给正确的派生类型构造函数。 在此之后,我需要根据数据包的类型引发事件。 如何在不使用is运算符的情况下执行此操作? 我的方法听起来健全吗?


访客模式 ,当然! 谢谢Pablo Romeo 。

在这种情况下,我使控制器,即调用工厂方法,是访问者。 我的结果:

 public interface IPacketHandler { void Handle(FooPacket p); void Handle(BarPacket p); } public class Controller : IPacketHandler { OnDataReceived() { RcvPacket p = RcvPacket.ReadPacket(comport); // Call factory method p.Handle(this); // *** Virtual Call: The first dispatch *** } // Note the use of explicit interface implementation here. IPacketHandler.Handle(FooPacket p) { OnFoo(); } IPacketHandler.Handle(BarPacket p) { OnBar(); } } public abstract class RcvPacket { public static RcvPacket ReadPacket(...) { ... } // Factory method public void Handle(IPacketHandler handler); } public class FooPacket : RcvPacket { public override void Handle(IPacketHandler handler) { handler.Handle(this); // *** Overloaded call: The second dispatch *** } } public class BarPacket : RcvPacket { public override void Handle(IPacketHandler handler) { handler.Handle(this); // *** Overloaded call: The second dispatch *** } } 

这里有趣的是,通过显式实现访问者界面, Handle调用基本上是隐藏的。 来自MSDN :

实现接口的类可以显式实现该接口的成员。 当显式实现成员时,不能通过类实例访问它,而只能通过接口的实例访问它。

好吧,考虑到你可能不希望在每个数据包中有更多的额外逻辑,你可以通过Double Dispatch完成它。

在那种情况下,我可能会创建一个界面,例如:

 public interface IPacketEvents { void On(Foo foo); void On(Bar bar); } 

我假设你有一个所有数据包的基类。 在其中我会声明:

 public abstract void RaiseUsing(IPacketEvents events); 

包的每个子类都将实现以下内容:

 public override void RaiseUsing(IPacketEvents events) { events.On(this); } 

你可以有一个新的类实现IPacketEvents或者你调用工厂可以实现它的同一个类。 在第二种情况下,您的通话最终会:

 OnDataReceived: RcvPacket p = RcvPacket.ReadPacket(comport); // Call factory method p.RaiseUsing(this); 

使用这种类型的调度,你得到的是每种类型调用相应的方法,因为它“知道”要调用哪一个。 我可能会对所有方法使用相同的“On”名称感到困惑,但这并非完全必要。 它们可以是OnFoo()和OnBar()。

此类行为也用于访问者模式 。

我能想到的唯一方法是将OnFooOnBar的实现移动到RcvPacketFooRcvPacketBar类。

 public class RcvPacket{ public abstract void On(RcvPacketHandler eh); } public class RcvPacketFoo : RcvPacket { public override void On(RcvPacketHandler eh){eh.OnFoo();} //OnFoo implemenation } public class RcvPacketBar : RcvPacket { public override void On(RcvPacketHandler eh){eh.OnBar();} //OnBar implemenation } //Update following your comment: public class RcvPacketHandler { public void OnFoo(){} public void OnBar(){} //.... OnDataReceived: RcvPacket p = RcvPacket.ReadPacket(comport); // Call factory method p.On(this);