3

我目前正在使用一些旧的 C# 代码,它基本上使用派生类型,其唯一目的是使用 Type 作为“属性”,例如:

public abstract class Fruit
{
    public int Property { get; set; }
}

public class Apple : Fruit {}

public class Pear : Fruit {}

接着:

public void Foo(Fruit item)
{
    if(item is Apple)
    {
        // do something
        return;
    }
    if(item is Pear)
    {
        // do something
        return;
    }

    throw new ArgumentOutOfRangeException("item");
}

我会在 BaseClass 上包含一个枚举属性来指定“类型”:

public class Fruit
{
    public int Property { get; set; }

    public FruitType Type { get; set; }
}

public enum FruitType
{
    Apple,
    Pear
}

然后这样使用它:

public void Foo(Fruit item)
{
    switch(item.Type)
    {
        case FruitType.Apple:
            // do something
            break;
        case FruitType.Pear:
            // do something
            break;
        default:
            throw new ArgumentOutOfRangeException();
    }
}

我觉得前一种模式是对继承的滥用,但是在重写这段代码之前我应该​​考虑它的任何优点吗?

4

1 回答 1

12

处理这种情况的标准“OO”方法是使 DoSomething 成为 Fruit 上的抽象方法。然后调用者只是调用 DoSomething,知道实现会做正确的事。

这种方法的缺点是它将计算用户可能想要的所有可能的“东西”的责任交给了抽象类的作者。

使用“访问者模式”可以减轻这种不利影响。访问者模式是使第三方能够根据值的运行时类型有效地切换行为的标准方式。你可以考虑研究一下。

您的第二种方法 - 用标签区分类型 - 很常见并且非常有效。Roslyn 广泛使用这种技术。OO纯粹主义者认为有点臭,但幸运的是我不是OO纯粹主义者。

我喜欢的第二种技术的变体是:

public enum FruitKind { Apple, Orange }
public abstract class Fruit
{
  private Fruit(FruitKind kind)
  {
      this.Kind = kind;
  }
  public FruitKind Kind { get; protected set; }
  private class Apple : Fruit
  {
      public Apple() : base(FruitKind.Apple) {}
  }
  public static Fruit MakeApple() { return new Apple(); }
  // similarly for orange
}

现在Fruit 用户可以确定类型的唯一方法是通过标签,因为 Apple 和 Orange 不可访问。您知道没有第三方会制作自己的 Fruit,因为唯一的 Fruit 构造函数是私有的。

于 2013-01-22T00:42:45.890 回答