根据类型切换行为的最佳方法

时间:2012-04-11 22:44:28

标签: c# polymorphism

  

可能重复:
  C# - Is there a better alternative than this to ‘switch on type’?

考虑经典:

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

在大多数情况下,在我的用户界面中,我可以将所有Widget视为相同。但是,我需要ifswitch通过。

可能的方法:

枚举指标 - 由构造函数

设置
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的使用,我最初觉得不对。但是, 是我在单元格中指定的属性的类型,所以当然这是有道理的!

今天办公室似乎太长了......

3 个答案:

答案 0 :(得分:9)

还有另一种可能性。使用虚拟调度:

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();
    }
} 

答案 1 :(得分:5)

Subtype polymorphism是最好的解决方案,避免这种检查是OO创建的主要原因之一。

Widget可能有方法DoSomething()(可能是摘要),然后RedWidgetBlueWidget会覆盖它。

另见Martin Fowler的Replace Conditional with Polymorphism

  

看过:你有一个条件,根据对象的类型选择不同的行为。

     

重构:将条件的每一条腿移动到子类中的重写方法。使原始方法抽象化。

答案 2 :(得分:0)

对于Edit#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)。