C#:通过传递类型(从另一个静态对象检索的类型)进行的通用工厂对象实例化

C#:通过传递类型(从另一个静态对象检索的类型)进行的通用工厂对象实例化,c#,generics,types,interface,casting,C#,Generics,Types,Interface,Casting,我创建了一个小抽象域来说明我所面临的问题,就是这样 有一个中世纪的游戏,玩家是他们军队的将军,整个战斗主要受战斗计划的影响,这是在战斗开始之前制定的,比如说准备模式 为了实现所需的功能,我创建了一个接口IBattleUnit,并使事情非常简单: 公共接口单元 { 无效移动(); 无效攻击(); 字符串敬礼(); } 现在有三种类型的单位可以完成任务,因此,Archer.cs、Pikeman.cs和browsman.cs以几乎相同的方式实现界面: 公共级剑客:iBattle单位 { 私人剑客()

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

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

为了实现所需的功能,我创建了一个接口
IBattleUnit
,并使事情非常简单:

公共接口单元
{
无效移动();
无效攻击();
字符串敬礼();
}
现在有三种类型的单位可以完成任务,因此,
Archer.cs
Pikeman.cs
browsman.cs
以几乎相同的方式实现界面:

公共级剑客:iBattle单位
{
私人剑客(){}
公开作废动议()
{
//剑客招式
}
公开无效攻击()
{
//剑客攻击
}
公共字符串敬礼()
{
返回“剑客为您服务,大师。”;
}
}
请注意,私人构造器,它仅用于在
兵营
招募作战部队,这是通用工厂

公共静态类营房,其中T:类,单位
{
私有静态只读Func UnitTemplate=Expression.Lambda(
表达式.New(typeof(T)),null.Compile();
公共静态T recrupt()
{
返回UnitTemplate();
}
}
注意:空构造函数的预编译lambda表达式使(在我的机器上)单位创建速度更快,而陆军可以变得非常大,快速的通用创建正是我想要实现的

由于涵盖了一场战斗需要开始的所有内容,
作战计划
说明是唯一缺少的部分,因此我们来:

公共静态类作战计划
{
私有静态列表类型;
私人静态列表_其他接口管理员;
//...
专用静态字典(u)首选项;
专用静态类型_首选作战单元;
专用静态类型_首选传输单元;
//...
静态作战计划
{
//从文件中读取作战计划(或计划初始数据的来源)
//探索各种接口实现程序集
//最后填写所有字段
_首选作战单位=类型(弓箭手);
}
公共静态类型首选作战单元
{
得到
{
返回-首选作战单元;
}
}
//……等等
}
现在,如果你已经做到了这一点,你就知道整个领域-它甚至可以编译,一切看起来都很光明,直到

到目前为止:我创建了一个控制台应用程序,添加了对上面提到的内容的引用,并试图从中获益。 为了完整描述我的困惑,我首先注意到的作用是什么:

  • 如果我想让兵营给我一个特定的战斗单位,我可以实例化它,让它战斗、移动和敬礼。如果以这种方式进行实例化:
IBattleUnit=兵营.新兵();
  • 如果我想知道基于作战计划的首选单位是什么,我可以得到它,我可以要求它的
    AssemblyQualifiedName
    ,我得到类型(事实上它是
    Archer
    ,就像它停留在
    作战计划中一样),长话短说,我得到了我期望的,当我呼叫:
Type preferedType=BattlePlan.PreferedBattleUnit;

在这里,当我希望作战计划向我提供一个类型,而我只是将该类型传递给兵营以实例化某种类型的部队时,VisualStudio2012(当前版本的resharper)阻止了我,并没有编译代码,而导致错误的代码是:

Type t=Type.GetType(作战计划.PreferedBattleUnit.AssemblyQualifiedName);
IBattleUnit u=兵营.新兵();
无论我做什么,无论我是通过
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

因此,对于实际问题:

  • 有没有办法,我可以将类型传递给兵营,而不必更改底层基础设施
  • 或者我有没有故意做错什么

  • 在过去的两天里,我一直在谷歌上四处搜索,唯一明确的办法就是改变兵营,事实上这是我不想做的

    编辑1号:当重新思考概念和一切时:
    IBattleUnit
    首先被描述为每个单位都能完成的一组核心作战行动(我们希望这样)。我不想引入基类,只是因为我知道,可能会有
    GroundUnitBase
    FlyingUnitBase
    抽象类。为了方便起见,我们希望有清晰的逻辑设计。。。但是绝对只能有一个静态的
    营房


    对于作战单位来说,现在在我眼里放一个基类似乎可以改变代码的可运行性,我正在尝试这一点。。。阅读时,我写的东西让我想到了
    UnitBase
    类可能对设计甚至都没有帮助,但在某种程度上对其可编译性有帮助。因此,这是我在重新思考所写内容后的第一个想法。

    你可以使用反射来实现这一点,比如:

    public static class Barracks
    {
        public static IBattleUnit Recruit(Type preferredType)
        {
            return (IBattleUnit)typeof(Barracks<>).MakeGenericType(preferredType).GetMethod("Recruit", BindingFlags.Public|BindingFlags.Static).Invoke(null,null);
        }
    }
    
    IBattleUnit unit = typeof(Barracks).GetMethod("Recruit").MakeGenericType(BattlePlan.PreferedBattleUnit).Invoke(null, null) as IBattleUnit;
    

    如果你有一个
    PreferedBattleUnit
    的实例,你只需要使用
    dynamic
    关键字。请看一看这个问题(John Skeet回答):(编辑:这可能没有多大帮助,因为您的方法不是通用的)

    如果您没有对象的实例,请查看以下问题(同样,John Skeet ans
    IBattleUnit unit = typeof(Barracks).GetMethod("Recruit").MakeGenericType(BattlePlan.PreferedBattleUnit).Invoke(null, null) as IBattleUnit;
    
    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());
        }
    }