【问题标题】:C# - How to designate an enum as must be implemented in abstract class?C# - 如何指定必须在抽象类中实现的枚举?
【发布时间】:2016-10-04 12:09:07
【问题描述】:

例如,我有一个工厂类:

abstract class UnitFactory<T> where T:Unit
{
   abstract enum UnitType;

   public abstract T GetUnit(UnitType type);
}

这里,派生类必须有一个枚举(当然abstract enum不起作用)来指示它可以制作哪种单元,而不是我认为的一堆字符串常量难以执行/管理。

所以我的问题是如何制作这样的 "abstract" 枚举?或者如果不可能,做类似事情的最佳做法是什么?

抱歉我的英语不好和我看似愚蠢的问题。

编辑: 示例子类:

class ArcherFactory : UnitFactory<Archer>
{
   private static Archer _baseLongbowman = ....;
   private static Archer _baseCrossbowman = ....;

   // Child class must have a implementation of UnitType enum to
   // tell the user that it can only make those kind of units.
   public enum UnitType{ Longbowman, Crossbowman }

   public override Archer getUnit(UnitType type)
   {
      if (type == UnitType.Longbowman) return _baseLongbowman.Clone(...);
      return _baseCrossbowman.Clone(...);
   }
}

【问题讨论】:

  • abstract enum 是什么意思?枚举不支持继承。它们只是价值观
  • enum 是一个值类型,遗憾的是你不能从中派生任何东西
  • 这听起来像是XY Problem 的情况。 X 有问题,假设 Y 是解决方案(抽象枚举),当这不起作用时,要求 Y,而不是实际问题 X。实际问题是什么?
  • 我明白了,我只是想要一个“抽象枚举”之类的东西,一个必须实现的枚举。
  • @Tr1et:什么是“已实现的枚举”?提供一个示例,说明您将如何 1) 实例化多个具体工厂和 2) 为工厂/单元类型的几种组合调用 GetUnit

标签: c# enums factory


【解决方案1】:

你应该为你的抽象工厂定义两个泛型类型

public abstract class UnitFactory<TType, TUnit> where TUnit:Unit
{
    public abstract TUnit GetUnit(TType type);
}

然后你需要在你的archer工厂之外暴露archer类型,否则它就不能用了。

public enum ArcherType { Longbowman, Crossbowman }

并最终创建弓箭手工厂。

public class ArcherFactory : UnitFactory<ArcherType, Archer>
{
    private static Archer _baseLongbowman = ....;
    private static Archer _baseCrossbowman = ....;

    public override Archer GetUnit(ArcherType type)
    {
        switch (type)
        {
            case ArcherType.Crossbowman:
                return  _baseCrossbowman.Clone(...);

            default:
                return  _baseLongbowman.Clone(...);

        }
    }
}

编辑:

您可以使用 Func 来创建每个单元的单独实例,而不是使用静态实例和克隆。

public class ArcherFactory : UnitFactory<ArcherType, Archer>
{
    public ArcherFactory()
    {
        this.Register(ArcherType.Longbowman, () => new Archer(...));
        this.Register(ArcherType.Crossbowman, () => new Archer(...));
    }
}

public abstract class UnitFactory<TType, TUnit>
{
    private readonly Dictionary<TType, Func<TUnit>> factoryMethods = new Dictionary<TType, Func<TUnit>>();

    protected void Register(TType type, Func<TUnit> constructorFuc)
    {
        // perform some sanity checks
        this.factoryMethods.Add(type, constructorFuc);
    }

    public TUnit GetUnit(TType type)
    {
        // perform some sanity checks
        return this.factoryMethods[type]();
    }
}

然后使用它

var archerFactory = new ArcherFactory();
var crossbowMan = archerFactory.GetUnit(ArcherType.Crossbowman); 

【讨论】:

  • 太棒了!!!!我真的没想过将枚举作为类型参数传递。但是我可以将第一个类型参数限制为枚举吗?
  • 虽然您应该只使用一个 UnitType 枚举并将不同的构造函数委托注册到具有不同枚举值的 1 个工厂......所以可以只使用 UnitFactory.Create(UnitType.LongBowman)。也不需要克隆等。
  • 添加了在工厂中注册 funcs 的示例,这将使您的生活更轻松,而无需担心克隆。
【解决方案2】:

要求每个具体工厂公开其自己的类型列表意味着所有具体实现都将单独绑定到该工厂。

换句话说,这样做绝对没有任何好处

var longbowman = archerFactory.GetUnit(ArcherFactory.UnitType.Longbowman);
var crossbowman = archerFactory.GetUnit(ArcherFactory.UnitType.Crossbowman);

比较简单:

// this is better than a huge switch/case method
var longbowman = archerFactory.GetLongbowman()
var crossbowman  = archerFactory.GetCrossbowman();

要使抽象工厂模式有意义,您需要将类型与工厂分开定义,即

enum ArcherType { Longbowman, Crossbowman }

interface IFactory<Ttype, Tvalue>
{
    Tvalue GetUnit(Ttype);
}

interface IArcherFactory : IFactory<ArcherType, Archer>
{
    ...
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-10-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-28
    • 1970-01-01
    • 2017-08-17
    • 1970-01-01
    相关资源
    最近更新 更多