4

为了尽可能容易地解决这个问题,我正在尝试实现一个通用池系统,只要它们实现了就可以处理任意数量的具体类IBaseComponent

因此,在我管理池的班级中,我有一个池字典:

Dictionary<Type, Pool<IBaseComponent>> pools;

因为这将允许我创建尽可能多的实现类IBaseComponent(可以这么说,这是一个非常“低级”的接口 - 所以实现它的类不会兼容得太远),并且可以有一个每个人的游泳池。

现在,我遇到的问题是第一次将 an 加载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 插入到字典中。

我对你们这些好人的问题是:有什么合理的方法可以解决这个问题吗?任何可能的方式,甚至?

如果我需要通过一些外部方法(XML、TXT 等)为池的至少第一个模板对象、池可以用于的每个可能的具体类加载元素,我只能访问顶级接口和类的正式类型(都在外部定义文件中定义),我可以在这里做什么吗?

或者这根本不可能?

4

2 回答 2

4

您使用的是 .Net 4+ 吗?如果是这样,您可以创建一个接口IPool<out T>。使out泛型参数协变,这意味着它将接受具有泛型参数 T 或派生自 T 的任何版本的接口。

出于某种原因,协/逆变仅适用于接口和委托,这就是您需要 IPool 的原因。

您的字典将变为:

Dictionary<Type, IPool<IBaseComponent>>() pools;

我在脑海中将它与反射结合起来有点麻烦,但我认为这应该可行。如果没有,请告诉我,我会花更多时间在我的测试代码上。

于 2012-11-27T04:45:14.290 回答
1

我正在玩弄的另一种选择是修改Pool<T>自己。

Pool<T>我将修改它以支持存储基于用于 T 的接口的任何兼容类,而不是只存储一种类型的(具体)类。

因此,Pool<IBaseComponent>将负责存储所有可能实现的类型IBaseComponent

在内部,它会将所有内容存储为IBaseComponent,但会保留对每个具体类型的存储位置的引用(在由Type类型要复杂得多])

我没有提到的一件事是,它IBaseComponent公开了两点功能,这是我准备一个以“盲目”方式使用的组件所需要的(即:将调用这个池的工厂在编译时不知道什么类型它正在使用的组件,它只是根据 XML 中定义的内容加载它们,或者从附加了这些组件的现有对象复制),即:(Deserialize从 XML/JSON/whatever 构建组件)和CopyInto(IBaseComponent other)(构建组件通过从另一个组件复制)。

因此,这仍然存在Pool无法将 动态IBaseComponent转换为调用者请求的Type问题,但这无关紧要。如果调用者真的提前知道硬编译时类型,它可以进行强制转换。如果调用者不这样做,那么除了无论如何公开的访问方法之外,它将无法做任何事情IBaseComponent

重要的是IBaseComponent返回Pool是正确的类型,这将处理。

简而言之:我将删除一些现代泛型(内部 Pool 仅适用于传入的类型,在外部它将只允许 T 成为接口),并替换它会很好的老式Type传递。必须在内部使用反射来实例化类型池,但我认为可以期望初始化或调整池大小将是一个非常昂贵的操作。

于 2012-11-27T14:53:41.530 回答