Дескриптор базового класса для экземпляра дочернего класса. Есть ли элегантный способ получить тип дочернего класса во время выполнения? - PullRequest
2 голосов
/ 30 июня 2010

Вот как я это делаю в настоящее время:

ref class Base abstract {};
ref class ConcreteClass1 : public Base {};
ref class ConcreteClass2 : public Base {};
(...and even more...)

void DoStuff(Base ^base)
{
   System::Type ^type = base->GetType();
   System::String ^name = type->Name;

   if(name == "ConcreteClass1")
          DoOtherStuff((ConcreteClass1 ^) base);
   else if(name == "ConcreteClass2")
          DoOtherStuff((ConcreteClass2 ^) base);
   (...and even more...)
}

Есть ли более "изящный" способ сделать это?

При моем подходе я должен добавить новое, еслидля каждого нового конкретного класса, который заставляет меня чувствовать себя как один из примеров на thedailywtf.com.

Ответы [ 3 ]

2 голосов
/ 30 июня 2010

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

void DoStuff(Base ^base)
{
   System::Type ^type = base->GetType();

   if(type == ConcreteClass1::typeid)
          DoOtherStuff((ConcreteClass1 ^) base);
   else if(type == ConcreteClass2::typeid)
          DoOtherStuff((ConcreteClass2 ^) base);
   (...and even more...)
}

Однако этонемало "кодового запаха" к нему.Как правило, весь смысл использования абстрактных классов состоит в том, чтобы учесть полиморфизм - если вы можете сделать DoOtherStuff виртуальной функцией для каждого типа, вы можете просто сделать:...

1 голос
/ 30 июня 2010

Я считаю, что лучше, чем операторы if, использовать таблицу диспетчеризации - по сути это словарь из «типа» для функции указатель / делегат. Если дочерний класс отвечает за регистрацию себя в таблице, тогда ваша функция отправки выглядит просто так:

void DoStuff(Base ^base)
{
    System::Type ^type = base->GetType();
    m_DispatchTable[type](base);
}

и для добавления новой отправки нужно просто зарегистрироваться в таблице - обновление кода не требуется. Это позволяет избежать сохранения агрегата «если», и, если вам когда-либо понадобится вызвать более одной функции, вы можете сделать вашу таблицу диспетчеризации более сложной.

1 голос
/ 30 июня 2010

Более элегантно, если ваш дизайн ограничивает все знания о том, что делает ConcreteClass1 особенным внутри ConcreteClass1. Могли бы вы иметь функцию Process() в базовом классе, которую каждый из них наследует от базы, и делать так, чтобы она делала все, что будут делать ваши тела if, включая вызов DoOtherStuff(this)? Это обеспечит лучшую инкапсуляцию, и когда вы добавите больше классов, вы не измените свой вызывающий код, потому что он будет просто вызывать base->Process() и полагаться на полиморфизм.

...