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

本文关键字:最佳 方法 换行 类型 | 更新日期: 2023-09-27 18:31:00

可能的重复项:
C# - 有没有比这更好的替代方法来"打开类型"?

考虑经典:

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

在大多数情况下,在我的 UI 中,我可以对所有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,最初对我来说感觉不对劲。但是,这是我在单元格中分配给的属性的类型,所以这当然是有道理的!

今天在办公室呆得太久了,似乎...

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

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

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的Replace Condition with Polymorphism:

已见:您有一个条件,该条件根据对象的类型选择不同的行为。

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

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