虚拟呼叫与类型检查的另一个例子(Another example of virtual calls v

2019-09-17 12:10发布

问题

我发誓,每次我把它捣烂成我的大脑,我应该使用虚拟电话与类型检查(如:

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 :

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

Answer 1:

那么,考虑到你可能不希望每个包里面很多额外的逻辑,则可以通过完成其双重分派 。

在这种情况下,我可能会创建一个接口,如:

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);

使用这种类型发稿时,你会得到什么,是每个类型,调用相应的方法,因为它“知道”调用哪一个。 它可能会混淆了一下,我使用了相同的“开”名进行的所有方法,但事实并非完全必要的。 他们可能是OnFoo()和OnBar会()。

这种类型的行为也被用在了访问者模式 。



Answer 2:

我能想到的唯一的办法就是实施移动OnFooOnBarRcvPacketFooRcvPacketBar类。

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);


文章来源: Another example of virtual calls vs. type-checking