【发布时间】:2013-11-28 03:52:10
【问题描述】:
我创建了一个小抽象域来说明我面临的问题,所以它就是这样。
有一个中世纪的游戏,玩家是他们军队的将军,整个战斗主要受战斗计划的影响,战斗计划是在战斗开始之前制定的,比如说准备模式。
为了实现所需要的,我创建了一个接口IBattleUnit 并使事情变得非常简单:
public interface IBattleUnit
{
void Move();
void Attack();
string Salute();
}
目前使用三种类型的单元就可以了,所以Archer.cs、Pikeman.cs 和Swordsman.cs 以几乎相同的方式实现接口:
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
}
现在,如果您达到了这一点,您就会了解整个域 - 它甚至可以编译并且一切看起来都很明亮,直到...
到目前为止:我创建了一个控制台应用程序,添加了对上述内容的引用,并尝试从引擎盖下的内容中获利。 为了完整描述我的困惑,我首先注意到 IS WORKING:
- 如果我想让兵营给我一个特定的 BattleUnit,我可以实例化它,让它战斗、移动和敬礼。如果以这种方式进行实例化:
IBattleUnit unit = Barracks<Pikeman>.Recruit();
- 如果我想知道基于作战计划的首选单位是什么,我可以得到它,我可以询问它的
AssemblyQualifiedName,我得到类型(实际上它是Archer,就像它留在BattlePlan) ,长话短说,当我打电话时,我得到了我所期望的:
Type preferedType = BattlePlan.PreferedBattleUnit;
在这里,当我希望 BattlePlan 为我提供一个 Type 而我只是将 Type 传递给 Barracks 以实例化某种单元时,VisualStudio2012(当前版本的更新器)阻止我并且不编译代码,而导致错误的代码是:
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 最初被描述为每个单位都能够执行的一组核心战斗行动(我们希望它能够做到)就这样)。我不想介绍基类,只是因为我知道,可能会有GroundUnitBase 和FlyingUnitBase 抽象类,我们希望有清晰和合乎逻辑的设计......但绝对必须只有一个静态的Barracks。
仍然适用于 BattleUnits - 现在将一个基类放在我的眼中似乎可以改变代码可运行的事情,我正在尝试这一点......阅读,我写的东西让我思考@ 987654345@ 类甚至可能对设计没有帮助,但在某种程度上它的可编译性。所以这是我重新思考所写内容后的第一个想法。
【问题讨论】:
-
基本问题是泛型参数在编译时必须保持不变。但是,我可能会建议您将接口的性质更改为 ISalute、IAtack 和 IMove,从而允许具有您想要的特定功能的具体战士。这将使您远离您遇到的问题以及基类。
标签: c# generics types interface casting