继承和铸造
本文关键字:继承 | 更新日期: 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
}
}
}
如果难以理解,请查找as
和is
关键字。
当然,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).
我认为你的类层次结构是正确的!