Наследование и кастинг - PullRequest
1 голос
/ 08 апреля 2011

У меня основная проблема с приведением унаследованных типов к базовому классу. Я знаю, что это обычно невозможно, т. Е. Вы можете привести производный класс к его базовому классу, но обратное неверно. Вот пример фрагмента, с которым я борюсь:

Допустим, я определил абстрактный класс для представления 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; }
}

и у меня есть массив слотов для представления всех слотов на компьютере:

PciCardSlot [] pciSlotsInComputer = new PciCardSlot[6];

Затем я определяю функцию для извлечения объекта 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;

Тогда у меня есть функция, которая выглядит следующим образом, которая предназначена для работы с объектом Модем:

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 к объекту Modem, производному от PciDevice.

Я читал об этом, и почти везде я смотрю, люди, кажется, думают, что если вам нужно привести базовый класс к классу-члену, у вас плохой дизайн. Моя иерархия классов плохо спроектирована? Какие-либо предложения? Спасибо за чтение.

Ответы [ 7 ]

7 голосов
/ 08 апреля 2011

Ну, я не думаю, что есть внутренняя проблема с полиморфным отношением к PciDevices.Есть много экземпляров, встроенных в каркас, где необходимо привести объект обратно к типу «известный контекст».

Однако BaudRate - это свойство только для модема, поэтому его определение и логика должны находиться в этом классе.,Не должно быть функции более высокого уровня с этой конкретной целью.

В общем, ваши функции get должны быть определениями свойств для объектов-владельцев.

По сути, вам нужно знать, гдемодем (ы) перед тем, как вы попытаетесь получить доступ к BaudRate.

Если, например, вы хотите обновить все BaudRates модема и класс модема был хорошо инкапсулирован, вы должны иметь возможность сделать что-то вроде

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

Если это трудно понять, найдите ключевые слова as и is.

Конечно, у Linq есть более современный способ сделать это, вот один из вдохновленных комментариями Криса.

void UpdateModemBaudRates(int baudRate)
{
    pciSlotsInComputer.Select(s => s.CardInSlot).OfType<Modem>().AsParallel().ForAll<Modem>(modem => modem.BaudRate=baudRate);
}
4 голосов
/ 08 апреля 2011

Эта часть, похоже, либо скопирована неправильно, либо не работает:

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 общедоступным, иначе вы не сможете получить к нему доступ - еще лучше сделать его государственная собственность.

1 голос
/ 08 апреля 2011

Я не понимаю, как этот код компилируется. Это пытается преобразовать PciCardSlot в PciDevice ....

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;
        }
1 голос
/ 08 апреля 2011

вы уверены, что передаете 1 для slotId?

Что произойдет, если вы измените setBaudRateForModem следующим образом:

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

используйте отладчик, чтобы определить тип возвращаемого устройства,это на самом деле тип модема?

1 голос
/ 08 апреля 2011

Вы можете привести производный класс к базовому классу, но вы не можете привести один производный тип к другому производному типу. Возьмите два класса B & C, оба получены из A.

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.
0 голосов
/ 08 апреля 2011

Вы должны проверить перед использованием if (getInsertedCard(slotId) is Modem).

Я думаю, что ваша иерархия классов в порядке!

0 голосов
/ 08 апреля 2011

Приведение к производному типу - это то, что довольно часто всплывает с коллекциями.До обобщения все коллекции обрабатывались таким образом.

В этом случае вы получаете ошибку времени выполнения, потому что комментарий I know that slot 1 contains a modem должен быть неправильным - поставьте точку останова на строке и проверьте, какой это тип на самом деле.

Мое предложение состоит в том, что, хотя иерархия имеет смысл, вы не должны помещать setBaudRateForModem в качестве метода в PciCardSlot.В тот момент, когда вы хотите позвонить setBaudRateForModem, вы уже должны полностью осознавать, что имеете дело с модемом - и так почему бы вам не использовать его в качестве модема?На самом деле не имеет смысла объединять несколько вызовов, таких как «setBaudRateForModem», «setPortForModem» и т. Д., - вы дублируете каждое свойство Модема внутри некоторого магического всезнающего класса.

Вместо этоговы думаете, что работаете с модемом - приведите его к модему, а затем получите к нему доступ напрямую.Бьет доступ к нему косвенно как целое число;).

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";
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...