使用泛型问题并使用运行时类型,而不是(通用)T

时间:2012-11-27 04:13:38

标签: c# generics

为了尽可能容易地解决问题,我正在尝试实现一个通用的池系统,只要它们实现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>我有权访问的是顶级接口和类的正式类型(都在外部定义文件中定义),我可以在这里做什么吗?

或者这根本不可能?

2 个答案:

答案 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路过。必须在内部使用反射来实例化类型池,但我认为可以预期初始化或调整池的大小是非常昂贵的。