2

问题

我发誓,每次我把它敲进我的大脑时,我都应该使用虚拟呼叫与类型检查(例如:

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


解决方案

访客模式,当然!谢谢巴勃罗·罗密欧

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

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

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

4

2 回答 2

4

我能想到的唯一方法是将 and 的实现OnFoo移到OnBarandRcvPacketFooRcvPacketBar

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);
于 2012-05-24T23:01:06.613 回答
2

好吧,考虑到您可能不希望每个数据包内有太多额外的逻辑,您可以通过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()。

这种类型的行为也用于访问者模式

于 2012-05-24T23:15:29.963 回答