关于c#:根据类型切换行为的最佳方式

Best way to switch behavior based on type

本问题已经有最佳答案,请猛点这里访问。

Possible Duplicate:
C# - Is there a better alternative than this to ‘switch on type’?

考虑经典:

1
2
3
class Widget { }
class RedWidget : Widget { }
class BlueWidget : Widget { }

在我的用户界面中,大多数情况下,我可以对所有的Widget进行相同的处理。但是,有一些小的差异,我需要通过ifswitch来解决。

可能的方法:

枚举指示器-由构造函数设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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

1
2
3
4
5
6
7
Widget w = ...;
if (w is RedWidget) {
    (RedWidget)w ...
}
else if (w is BlueWidget) {
    (BlueWidget)w ...
}

我之所以这么做是因为1)大部分代码都是这样写的,但是更糟糕。2)90%的代码是相同的-基本上,网格视图中的一列需要根据类型进行不同的处理。

你会推荐哪一个?(或者谁有更好的解决方案?)

编辑我知道我可能会被推荐给访问者模式,但对于这种情况下的稀疏、微小的差异来说,这似乎很复杂。

编辑2所以有一个特别的区别,我在整理时遇到了困难,那就是这个列在这两种类型之间是不同的。在一种情况下,它检索一个bool值,并将其分配给网格单元。在另一种情况下,它得到一个字符串值。

我想在这种情况下,很明显我可以定义:

1
2
3
4
5
public object virtual GetColumn4Data();

public override GetColumn4Data() { return m_boolval; }

public override GetColumn4Data() { return m_mystring; }

由于使用了object,我最初觉得这是错误的。但是,这是我在单元中分配给的属性的类型,所以这当然是有意义的!

今天在办公室呆得太久了,好像…


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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();
    }
}


子类型多态性是最好的解决方案,避免这种检查是创建OO的主要原因之一。

Widget可能有一个方法DoSomething()(可能是抽象的),然后RedWidgetBlueWidget会覆盖它。

另见Martin Fowler的用多态性替换条件:

Seen: You have a conditional that chooses different behavior depending on the type of an object.

Refactor: Move each leg of the conditional to an overriding method in a subclass. Make the original method abstract.


对于编辑2下的问题,您可以使用一个通用类使类型在子类中有所不同,尽管根据您的设计,它可能适用,也可能不适用。这可能会导致其他艰难的设计决策。

粗略实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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)。