我创建了一个抽象域来说明我面临的问题,所以它就是这样。

有一个中世纪的游戏,玩家是他们军队的将军,整个战斗主要受战斗计划的影响,战斗计划是在战斗开始之前制定的,比如说准备模式。

为了实现所需,我创建了一个接口(interface) IBattleUnit 并使事情变得非常简单:

public interface IBattleUnit
{
    void Move();
    void Attack();
    string Salute();
}

现在有三种类型的单元可以完成这项工作,所以 Archer.csPikeman.csSwordsman.cs 以几乎相同的方式实现接口(interface):

public class Swordsman : IBattleUnit
{
    private Swordsman() {}

    public void Move()
    {
        //swordsman moves
    }

    public void Attack()
    {
        //swordsman attacks
    }

    public string Salute()
    {
        return "Swordsman at your service, master.";
    }
}

请注意 私有(private) 构造函数,它用于仅在 Barracks 中招募战斗单位,这是通用工厂

public static class Barracks<T> where T : class, IBattleUnit
{
    private static readonly Func<T> UnitTemplate = Expression.Lambda<Func<T>>(
       Expression.New(typeof(T)), null).Compile();

    public static T Recruit()
    {
        return UnitTemplate();
    }
}

注意:空构造函数的预编译 lambda 表达式使(在我的机器上)单元创建速度更快,而军队可以变得非常庞大,快速的泛型创建正是我想要实现的。

由于已经涵盖了战斗需要开始的所有内容,BattlePlan 解释是唯一缺少的部分,所以我们来了:

public static class BattlePlan
{
    private static List<Type> _battleUnitTypes;
    private static List<Type> _otherInterfaceImplementors;
    //...

    private static Dictionary<string, string> _battlePlanPreferences;

    private static Type _preferedBattleUnit;
    private static Type _preferedTransportationUnit;
    //...

    static BattlePlan()
    {
        //read the battle plan from file (or whereever the plan init data originate from)
        //explore assemblies for interface implementors of all kinds
        //and finally fill in all fields
        _preferedBattleUnit = typeof (Archer);
    }

    public static Type PreferedBattleUnit
    {
        get
        {
            return _preferedBattleUnit;
        }
    }

    //... and so on

}

现在,如果您达到了这一点,您就会了解整个域 - 它甚至可以编译并且一切看起来都很明亮,直到......

到目前为止:我创建了一个控制台应用程序,添加对上述内容的引用,并尝试从幕后的内容中获利。
为了完整描述我的困惑,我首先注意到 正在起作用 :
  • 如果我想让兵营给我一个特定的 BattleUnit,我可以实例化它,让它战斗、移动和敬礼。如果实例化是这样完成的:

  • IBattleUnit unit = Barracks<Pikeman>.Recruit();
    
  • 如果我想知道基于作战计划的首选单位是什么,我可以得到它,我可以要求它的 AssemblyQualifiedName ,我得到类型(实际上它是 Archer ,就像它留在 BattlePlan 中一样),长话短说,当我打电话时,我得到了我期望的结果:

  • Type preferedType = BattlePlan.PreferedBattleUnit;
    

    在这里,当我希望 BattlePlan 为我提供一个 Type 而我只是将 Type 传递给 Barracks 以实例化某种 Unit 时,VisualStudio2012(当前版本的 resharper)阻止了我并且不编译代码,而代码,导致错误的是:

    Type t = Type.GetType(BattlePlan.PreferedBattleUnit.AssemblyQualifiedName);
    IBattleUnit u = Barracks<t>.Recruit();
    

    无论我做什么,无论我是通过 t ,还是将其作为 typeof(t) 传递,或者尝试将其转换为 IRepository ......我仍然无法编译这样的代码,(至少)有两个错误错误列表:
    Error   1   Cannot implicitly convert type 't' to 'BattleUnits.cs.IBattleUnit' Program.cs
    Error   2   The type or namespace name 't' could not be found (are you missing a using directive or an assembly reference?) Program.cs
    

    所以对于实际问题:
  • 有什么方法可以将类型传递给 Barracks,而不必更改底层基础设施?
  • 还是我在设计上做错了什么?


  • 过去两天我一直在谷歌上搜索,唯一明确的方法是改变兵营,这实际上是我不想做的。

    编辑第 1 号 :当重新思考概念和一切时:IBattleUnit 首先被描述为一组每个单位都可以执行的核心战斗行动(我们希望它是这样)。我不想引入基类,只是因为我知道,可能会有 GroundUnitBaseFlyingUnitBase 抽象类,我们希望有清晰的逻辑设计......但绝对必须只有一个静态 Barracks

    仍然是 BattleUnits - 现在将一个基类放在我的眼中似乎可以改变可运行代码的事情,我正在尝试这样做......阅读,我写的内容让我想到了 UnitBase 类可能甚至不是设计,而是在某种程度上帮助它的可编译性。所以这是我重新思考所写内容后的第一个想法。

    最佳答案

    你真的不需要 Barracks 是通用的。
    此解决方案不使用反射,因此效率更高:

    public static class Barracks
    {
        private static readonly IDictionary<Type, Func<IBattleUnit>> FactoryMethods = new Dictionary<Type, Func<IBattleUnit>>();
    
        public static void Register<T>(Func<IBattleUnit> factory) where T : IBattleUnit
        {
            FactoryMethods.Add(typeof(T), factory);
        }
    
        public static IBattleUnit Recruit<T>() where T : IBattleUnit
        {
            return Recruit(typeof (T));
        }
    
        public static IBattleUnit Recruit(Type type)
        {
            Func<IBattleUnit> createBattleUnit;
            if (FactoryMethods.TryGetValue(type, out createBattleUnit))
            {
                return createBattleUnit();
            }
    
            throw new ArgumentException();
        }
    }
    
    public class Swordsman : IBattleUnit
    {
        static Swordsman()
        {
            Barracks.Register<Swordsman>(() => new Swordsman());
        }
    }
    

    关于C# : Generic factory object instantiation by passing type (type retrieved from another static object),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/19989475/

    10-11 02:23