继承和铸造

本文关键字:继承 | 更新日期: 2023-09-27 17:49:21

我在将继承类型转换为基类时遇到了一个基本问题。我知道这通常是不可能的,也就是说,你可以将派生类转换为其基类,但相反的情况是不正确的。下面是我正在努力处理的一个示例片段:

假设我已经定义了一个抽象类来表示计算机PCI卡:

public abstract class PciDevice
{
   public abstract int GetDeviceId();
   public abstract String GetVendorName();
}
现在我创建了3种类型的继承类(设备):
public class GraphicsCard : PciDevice
{
   public override int GetDeviceId() { return 1666; }
   public override String GetVendorName() { return "CoolCorp"; }
   int trianglesPerSecond;
   ChipsetTypeEnum chipsetType;
}
public class AudioCard : PciDevice
{
   public override int GetDeviceId() { return 1999; }
   public override String GetVendorName() { return "ShinyCorp"; }
   int numChannels;
   int samplingRate;
}
public class Modem : PciDevice
{
   public override int GetDeviceId() { return 1234; }
   public override String GetVendorName() { return "BoringCorp"; }
   public int baudRate;
   bool faxEnabled;
}

现在,我在计算机内部定义一个"插槽":

public class PciCardSlot
{
   private int slotId;
   private int clockFreq;
   private PciDevice cardInSlot;
   public PciDevice getCard() { return cardInSlot; }
   public void setCard(PciDevice card) { cardInSlot = card; }
}

和我有一个slot数组来表示计算机中的所有slot:

PciCardSlot [] pciSlotsInComputer = new PciCardSlot[6];

然后,定义一个函数来检索给定槽id的PciDevice对象:

public PciDevice getInsertedCard(int slotId)
{
   return pciSlotsInComputer[slotId].getCard();
}

到目前为止一切顺利。现在,在代码的某个地方,我实例化了AudioCard、GraphicsCard和Modem对象,并将它们分配给插槽。

AudioCard a = new AudioCard();
Modem b = new Modem();
GraphicsCard c = new GraphicsCard();
PciCardSlot s0 = new PciCardSlot(); s0.setCard(a);
PciCardSlot s1 = new PciCardSlot(); s1.setCard(b);
PciCardSlot s2 = new PciCardSlot(); s2.setCard(c);
pciSlotsInComputer[0] = s0; pciSlotsInComputer[1] = s1; pciSlotsInComputer[2] = s2;

然后,我有一个函数,看起来像下面这样,它旨在对Modem对象进行操作:

public setBaudRateForModem(int slotId, int rate)
{
   ((Modem)getInsertedCard(slotId)).baudRate = rate; // Can not cast!!!
}
...
// I know that slot 1 contains a modem, so I do:
setBaudRateForModem(1, 9600);

上面的强制转换不起作用,因为我试图从PciDevice对象强制转换为从PciDevice派生的Modem对象。

我一直在阅读这方面的文章,几乎在我看到的所有地方,人们似乎都认为,如果你需要将基类强制转换为成员类,那么你的设计就很糟糕。我的类层次结构设计得不好吗?有什么建议吗?谢谢阅读

继承和铸造

嗯,我不认为对PciDevices进行多态处理有什么内在问题。框架中有很多实例需要将对象强制转换回"上下文已知"类型。

然而,波特率是一个仅限调制解调器的属性,所以它的定义和逻辑应该驻留在那个类中。不应该有更高级别的函数具有特定的目的。

一般来说,你的get函数应该是在拥有对象的属性上的get定义。

实际上,在您尝试访问波特率之前,您需要知道调制解调器在哪里。

例如,如果您想要更新所有Modem的波特率,并且Modem类被很好地封装,您应该能够做像

这样的事情
void UpdateModemBaudRates(int baudRate)
{
    foreach(PciCardSlot slot in pciSlotsInComputer)
    {
        Modem modem = slot.CardInSlot as Modem;
        if(modem != null)
        {
            modem.BaudRate = baudRate
        }
    }
}

如果难以理解,请查找asis关键字。

当然,Linq有一种更现代的方式来做到这一点,这里是一个受Chris评论启发的方法。

void UpdateModemBaudRates(int baudRate)
{
    pciSlotsInComputer.Select(s => s.CardInSlot).OfType<Modem>().AsParallel().ForAll<Modem>(modem => modem.BaudRate=baudRate);
}

这部分似乎是复制错误或没有功能:

PciCardSlot [] pciSlotsInComputer = new PciCardSlot[6];
public PciDevice getInsertedCard(int slotId) 
{
   return pciSlotsInComputer[slotId];
}

你声称返回一个PciDevice的对象,但它实际上是PciCardSlot类型——两个完全不相关的类,所以这不能编译。

你的演员在这里:

public setBaudRateForModem(int slotId, int rate) 
{
   ((Modem)getInsertedCard(slotId)).baudRate = rate; // Can not cast!!!
}

实际上是有效的,如果指定槽的对象实例确实是Modem,则

将工作-但您必须将baudRate设置为公共属性,否则您将无法访问它-最好将其设置为公共属性

可以将派生类强制转换为基类,但不能将一个派生类型强制转换为另一个派生类型。选两门B课;解析:

class B : A {}
class C: A {}

你可以实例化它们:

B object1 = new B();
C object2 = new C();
A base1 = (A)object1; // Casting to base.
A base2 = (A)object2; // Casting to base.
C newObect = (C)object1; // Fail. You cannot cast B to C as they are different classes.

您确定为slotId传递1吗?

如果你像这样改变setBaudRateForModem会发生什么呢?

public void setBaudRateForModem( int slotId, int rate ) {
  PciDevice device = getInsertedCard( slotId );
  Modem modem = device as Modem;
  if( null != modem )
  {
    modem.baudRate = rate;
  }
}

使用调试器来确定返回的设备类型。它实际上是调制解调器吗?

我不明白这段代码是如何编译的。这是试图将PciCardSlot转换为pcicdevice ....

public PciDevice getInsertedCard(int slotId) {
   return pciSlotsInComputer[slotId];
}

试试这个,把PciDevice属性改成public…

public class PciCardSlot
    {
        private int slotId;
        private int clockFreq;
        public PciDevice cardInSlot;
        public void setCard(PciDevice card)
        {
            cardInSlot = card;
        }
    }

然后也更改getInsertedCard…

public PciDevice getInsertedCard(int slotId)
        {
            return pciSlotsInComputer[slotId].cardInSlot;
        }

必须转换为派生类型是集合中经常出现的问题。在泛型出现之前,所有的集合都是这样处理的。

在这种情况下,您会得到一个运行时错误,因为注释I know that slot 1 contains a modem一定是错误的-在行上放置一个断点并检查它实际上是什么类型。

我的建议是,虽然层次结构是有意义的,你不应该把setBaudRateForModem作为PciCardSlot的方法。在您想要调用setBaudRateForModem时,您应该已经完全意识到您正在处理调制解调器—那么为什么不将其转换为调制解调器呢?把像setBaudRateForModem、setPortForModem等这样的多个调用串在一起是没有意义的——你是在把Modem的每个属性复制到某个神奇的无所不知的类中。

相反,当您认为正在使用调制解调器时-将其转换为调制解调器,然后直接访问它。

void ProcessModem(int slot)
{
  Modem modem = getInsertedCard(slot) as Modem;
  if (modem == null) throw new ArgumentException("slot is not a modem!");
  modem.BaudRate = 9600;
  modem.Port = "COM5";
}

在使用if (getInsertedCard(slotId) is Modem).

之前你应该检查一下

我认为你的类层次结构是正确的!