Лучший способ переключения поведения по типу
Возможный дубликат:
С# - Есть ли более эффективная альтернатива, чем это для типа включения?
Рассмотрим классику:
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
).