为了尽可能容易地解决问题,我正在尝试实现一个通用的池系统,只要它们实现IBaseComponent
就可以处理任意数量的具体类。
所以,在我管理池的类中,我有一个Pool of Pools:
Dictionary<Type, Pool<IBaseComponent>> pools;
因为这将允许我创建尽可能多的实现IBaseComponent
的类(这是一个非常“低级别”的接口,可以这么说 - 因此实现它的类不会太兼容)我想要,每个人都可以有一个游泳池。
现在,我正在运行的问题是第一次加载IBaseComponent
进入池,充当模板,可以这么说。
这个模板对象是从XML而不是代码加载的,所以我在编译时没有它的实际类,只是在运行时(它在XML定义中定义,我抓住了正式类型通过反思)。这一切都很好,但是,正如我们所知,泛型依赖于编译时的安全性。
所以,使用一些反思技巧,我有以下几点:
var type = typeof(MyChildComponent);
var genericType = typeof(Pool<>);
var specificType = genericType.MakeGenericType(type);
var pool = Activator.CreateInstance(specificType );
pools.Add(T, pool as Pool<IBaseComponent>);
假设有一些课程:
public class MyChildComponent : IBaseComponent
当我添加到池字典时,问题发生在第一个块的最后一行。实例化池到Pool<IBaseComponent>
的强制转换失败,导致将null插入到Dictionary中。
我对你这些好人的问题是:有什么合理的方法吗?任何可能的方式,甚至?
如果我需要通过一些外部方法(XML,TXT,无论如何)为至少池的第一个模板对象加载元素,对于可以使用Pool的每个可能的具体类,和< / em>我有权访问的是顶级接口和类的正式类型(都在外部定义文件中定义),我可以在这里做什么吗?
或者这根本不可能?
答案 0 :(得分:4)
您使用的是.Net 4+吗?如果是这样,您可以创建一个接口IPool<out T>
。 out
使泛型参数变为协变,这意味着它将接受任何版本的接口,其泛型参数为T或从T派生。
由于某种原因,协同/反演只适用于接口和代理,这就是你需要IPool的原因。
你的词典将成为:
Dictionary<Type, IPool<IBaseComponent>>() pools;
我在脑海中将它与反射结合起来有点麻烦,但我认为这应该有效。如果没有,请告诉我,我会花更多时间在我的测试代码上。
答案 1 :(得分:1)
我正在考虑的另一个选择是修改Pool<T>
本身。
我将修改它以支持存储基于用于T的接口的任何兼容类,而不是让Pool<T>
仅存储一种类型的(concerete)类。
因此,Pool<IBaseComponent>
将负责存储实现IBaseComponent
的所有可能类型。
在内部,它会将所有内容存储为IBaseComponent
,但会保留对每个具体类型存储位置的引用(在由Type
键入的列表字典中,也许,或者甚至只是一个大的线性列表[虽然这会使特定类型的“池”调整更复杂])
我忽略了一件事是IBaseComponent
暴露了两个功能点,这些功能都是我准备以“盲目”方式使用的组件所需要的(即:将调用此池的工厂在编译时不知道它正在使用哪些类型的组件,它只是根据XML中定义的内容加载它们,或者从附加了这些组件的现有对象进行复制),即:Deserialize
(从XML / JSON / what构建组件和CopyInto(IBaseComponent other)
(通过从另一个组件复制构建组件)。
因此,这仍然存在Pool
无法将IBaseComponent
动态转换为调用者请求的Type
的问题,但这无关紧要。如果调用者提前确实知道硬编译时类型,它可以进行强制转换。如果调用者没有,那么除了IBaseComponent
暴露的访问方法之外,它将无法做任何事情。
重要的是IBaseComponent
Pool
返回的是正确的类型,这将处理。
简单地说:我将删除一些现代泛型(从某种程度上说,Pool只适用于传入的类型,外部它只允许T作为一个接口),并且替换它将很好用Type
路过。必须在内部使用反射来实例化类型池,但我认为可以预期初始化或调整池的大小是非常昂贵的。