Лучший способ переключения поведения по типу

Возможный дубликат:
С# - Есть ли более эффективная альтернатива, чем это для типа включения?

Рассмотрим классику:

class Widget { }
class RedWidget : Widget { }
class BlueWidget : Widget { }

По большей части, в моем пользовательском интерфейсе, я могу рассматривать все Widget то же самое. Однако есть небольшие отличия, которые мне нужны if или switch через.

Возможные подходы:

Индикатор Enum - задается конструктором

enum WidgetVariety { Red, Blue }

class Widget {
    public WidgetVariety Variety { get; protected set; }
}

class RedWidget : Widget {
    public RedWidget() {
        Variety = Red;
    }
}

// Likewise for BlueWidget...

switch (mywidget.Variety) {
case WidgetVariety.Red:
    // Red specific GUI stuff

case WidgetVariety.Blue:
    // Blue specific GUI stuff
}

Использовать is

Widget w = ...;
if (w is RedWidget) {
    (RedWidget)w ...
}
else if (w is BlueWidget) {
    (BlueWidget)w ...
}

Причина, по которой я прибегал к этому, - это 1) Большая часть кода уже несколько написана таким образом, но намного уродливее. 2) 90% кода идентичны - в основном только один столбец в GridView должен обрабатываться по-разному в зависимости от типа.

Что бы вы порекомендовали? (Или у кого-то есть лучшее решение?)


Изменить Я знаю, что, вероятно, мне порекомендуется шаблон посетителя, но это просто кажется сложным для редких, незначительных различий в этом случае.

Изменить 2 Таким образом, одна конкретная разница, которую я испытывал с трудом, - это столбец, который отличается от двух типов. В одном случае он извлекает значение bool и присваивает это ячейке сетки. В другом случае он получает строковое значение.

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

public object virtual GetColumn4Data();

public override GetColumn4Data() { return m_boolval; }

public override GetColumn4Data() { return m_mystring; }

Это сначала мне не нравилось, из-за использования object. Однако это тип свойства, которое я назначаю в ячейке, поэтому , конечно это имеет смысл!

Слишком долго в офисе сегодня кажется...

Ответ 1

Есть и другая возможность. Использовать виртуальную отправку:

class Widget
{
    public virtual void GuiStuff() { }
} 
class RedWidget : Widget
{
    public override void GuiStuff()
    {
        //... red-specific GUI stuff
        base.GuiStuff();
    }
} 
class BlueWidget : Widget
{
    public override void GuiStuff()
    {
        //... blue-specific GUI stuff
        base.GuiStuff();
    }
} 

Ответ 2

Политизм подтипов - лучшее решение, избегая такого рода проверок, является одной из основных причин создания OO.

Widget может иметь метод DoSomething() (абстрактно, вероятно), а затем RedWidget и BlueWidget будут переопределять его.

Также см. Мартин Фаулер Заменить условный с полиморфизмом:

Видно: у вас есть условие, которое выбирает различное поведение в зависимости от типа объекта.

Рефакторинг: переместите каждую ветвь условного в метод переопределения в подклассе. Сделайте оригинальный абстрактный метод.

Ответ 3

Для вашего вопроса в разделе "Редактировать № 2" вы можете использовать общий класс, чтобы сделать тип различий между подклассами, хотя он может работать или не работать для вас в зависимости от вашего дизайна. Это, вероятно, приведет к другим жестким проектным решениям.

Пример:

internal abstract class BaseClass
{
   protected object mValue; // could also be defined as a T in BaseClass<T>

   public object GetColumn4Data { get { return mValue; } }
}

// this is a group of classes with varying type
internal abstract class BaseClass<T> : BaseClass
{
   public T GetTypedColumn4Data 
   {
      get { return (T)mValue; } 
      set { mValue = value; }
   }
}

// these are not really necessary if you don't plan to extend them further
// in that case, you would mark BaseClass<T> sealed instead of abstract
internal sealed class BoolSubClass : BaseClass<bool>
{
   // no override necessary so far
}

internal sealed class StringSubClass : BaseClass<string>
{
   // no override necessary so far
}

Обратите внимание, однако, что вы действительно не можете получить один ссылочный тип, который будет иметь различный тип возвращаемого значения для некоторого свойства или метода. Ссылка BaseClass в лучшем случае возвращает общий тип (например, object).