如何从基类调用派生类的方法?

22

我已经阅读了几篇类似的问题,但都无法解决我面临的问题。典型的答案是将其强制转换为派生类,但我不能这样做,因为我不知道派生类的类型。

以下是我的示例:

class WireLessDevice { // base class
    void websocket.parsemessage(); // inserts data into the wireless device object
}

class WiFi : WireLessDevice { // derived class
    GPSLoc Loc;
}

无线设备可以用于制作蓝牙、Wi-Max、蜂窝等设备,因此我不知道哪种类型的无线设备将接收数据。

当基类中的websocket接收到GPS数据包时,我需要更新派生设备的位置。

我考虑通过队列发送消息或创建事件处理程序并在事件参数中发送位置,但当数据留在类内部时,这些方法似乎有点笨重。

是否有一些内置到语言中的东西可以让我在不知道类型的情况下从基类调用我的派生设备?


看一下装饰器模式。你的问题似乎是你的设计有误。 - Reza Owliaei
6
不需要装饰器...看一下抽象方法或虚拟方法... - Chris Pfohl
2个回答

40

正确的方法是在基类中添加一个名为DoSomeMagic()的方法,带有默认实现或抽象实现。 派生类应该重写它来执行自己的操作。

可能会像这样:

public class WireLessDevice
{ // base class
    protected virtual void ParseMessage()
    {
        // Do common stuff in here
    }
}

public class WiFi : WireLessDevice
{ // derived class
    override void ParseMessage()
    {
        base.ParseMessage();//Call this if you need some operations from base impl.
        DoGPSStuff();
    }
    private void DoGPSStuff()
    {
        //some gps stuff
    }
    GPSLoc Loc;
}

如果我从基类websocket.parsemessage()调用this.setGPSLocation,它会自动调用派生类的setGPSLocation吗? - CramerTV
我不会从基类中调用SetGPSLocation,而是在派生类中调用。我将更新我的答案来演示。 - omer schleifer
不是很确定,但我认为“虚拟”方法必须有一个声明的主体。只有“抽象”方法没有主体。 - LightStriker
正确,这应该是带有主体或抽象的虚拟函数。 - omer schleifer
Omer,您的更新看起来很有前途。我不想使用抽象方法并将整个ParseMessage方法复制到所有派生类中-这是大量需要复制和维护的代码。您所做的看起来正是我需要的,但是您所说的“带有主体的虚拟”是什么意思?或者您是否已更新示例以包括该内容? - CramerTV
意思是 virtual void foo(); 不被允许(void foo(); 也不行),你要么定义一个抽象方法:abstract void foo(); 要么添加函数体:void foo() {//这是函数体}。祝好运! - omer schleifer

3

虚方法是一个好的解决方案。您可以在基类中创建一个名为ParseMessage的方法,处理所有派生类型的共同功能,并针对特定设备进行覆盖。

为了仍然能够处理其他消息类型,您需要根据需要调用base.ParseMessage

class WireLessDevice
{
    protected virtual bool ParseMessage(byte[] message)
    {
        // Inspect message and handle the known ones
        return true; // Only if message parsed, otherwise false
    }
}

class WiFi : WireLessDevice
{
    GPSLoc Loc;

    protected override bool ParseMessage(byte[] message)      
    {
        bool messageParsed = base.ParseMessage(message)
        if (!messageParsed)
        {
            // Handle device specific message with GPSLoc
            Loc = ...
        }
        else
        {
            return false;
        }
    }
}

如果您的基类没有处理其他消息,则代码可能会更简单(并且使用abstract)。但在这种情况下,我建议将Web套接字代码移动到派生类中。

1
这个解决方案与Omer的类似,我相信它会对我有用。我会尝试一下并更新回复。谢谢Thorarin! - CramerTV
@CramerTV 这就是你在别人编辑帖子时回答的结果,但是没错,它们几乎完全相同。我更喜欢我的解释,所以我保留了答案。 - Thorarin
太好了Thorarin。我修改了你的返回值,使其成为一个枚举类型,其中GPS是其中之一。通过这种方式,parseMessage例程将返回一个值,告诉接收到了什么类型的消息,并且只有在解析的消息对应于GPS坐标时才更新位置(我可能还有其他需要操作的“特殊情况”消息)。我还在枚举中包含了一个“错误”类型,以便我可以识别像你上面的示例那样的失败。谢谢你的回答。我给你们两个点赞,但选择了Omer的答案,因为他先回答了。 - CramerTV
1
伙计们,挑战当然是要自动化完成的。当然,你可以覆盖函数,然后(如果你想)在派生函数中调用base.function。我在想什么:在BASE函数中是否有一些话可以说:“好的,如果有人隐藏了这个函数,那么首先运行此函数,并且还要运行那个派生函数”。有没有办法做到这一点? - Fattie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接