在C#中,是否可以在运行时确定泛型的类型参数?

时间:2012-07-11 08:23:12

标签: c# .net generics

这就是我所拥有的(使用Rhino Mocks,但这不是问题的核心):

var entityMock = MockRepository.GenerateMock<IEntity>();
this.Cache = MockRepository.GenerateStub<Cache<IEntity>>();

是否可以更具体地设置Cache<T>的类型参数?类似的东西:

var entityMock = MockRepository.GenerateMock<IEntity>();
this.Cache = MockRepository.GenerateStub<Cache<typeof(entityMock)>>();

当然这不编译。但是,如果可能的话,我想使用Rhino Mocks生成的类型,这是IEntity的具体实现。

3 个答案:

答案 0 :(得分:2)

请注意,虽然提到了RhinoMocks,但 特定部分是GenerateStub,它围绕提供的类型创建了一个具体的实现;其余的是不可知的RhinoMocks。

通用类型参数在编译时解析 - 如您所知;这不允许在没有反射的情况下在线输入类型作为泛型参数(即:var list = new List<typeof(int)>();)。

但是,您可以使用反射创建泛型类型。在本质上,如果您可以通过以下方式获取动态代理的类型:

var entityMock = MockRepository.GenerateMock<IEntity>();
var dynamicType = entityMock.GetType();

MockRepository有一个GenerateStub,其中包含Typeobject[]个参数,因此从上面继续:

var cacheType = typeof(Cache<>);
var genericType = cacheType.MakeGenericType(dynamicType);
var stubbed = MockRepository.GenerateStub(genericType, null);

遗憾的是,stubbed项的类型为object,但正如Lucero在评论中所述,可以使用通用协方差来获得比{{1}更有用的类型}。我在下面演示了这一点。

<小时/> 基于与Lucero的有趣讨论,如果您定义一个新的object接口来表示Cache,那么通用协方差将允许您将生成的代理转换为基类型(ICache<out T>) :

ICache<IEntity>

答案 1 :(得分:2)

可以在运行时使用反射创建一个封闭的泛型类型。问题是你很可能只需要使用反射继续操作它,因为(鉴于它的类型在编译时是未知的)你不能把它当作可直接使用的东西。

例如,要创建“某事”列表:

public IList CreateList(Type t)
{
    var openListType = typeof(List<>);
    return (IList)openListType.MakeGenericType(t);
}

这个例子说明了几个要点:

  1. 如果在编译时指定了“目标类型”,则无法执行此操作。换句话说,CreateList无法接受t作为通用类型参数,仍然允许相同的功能。
  2. 在最坏的情况下,新实例不能输入除object以外的任何其他内容。在这里,我们知道我们将始终创建一个IList,所以事情会好一些。
  3. 我没有Rhino Mocks的经验,但在你的情况下,它会转化为:

    var entityMock = MockRepository.GenerateMock<IEntity>()
    var cacheType = typeof(Cache<>).MakeGenericType(entityMock.GetType());
    this.Cache = MockRepository.GenerateStub(cacheType);
    

    ...但仅当适当的GenerateStub重载可用时才会显示。

答案 2 :(得分:0)

如果代码希望根据泛型类型是否满足某些约束来执行不同的操作,特别是如果泛型类型中的某些操作甚至无法编译,则在某种程度上使用模式可能会有所帮助与Comparer<T>EqualityComparer<T>等类使用的类似。诀窍是使用一个带有泛型参数T的静态类,它将静态委托保存到一个参数类型为T的方法,并且具有静态方法,每个方法都采用可能受限制的方法类型U的通用参数,并且具有与U匹配T时与上述代理兼容的签名。第一次尝试使用静态类或其委托时,泛型类的静态构造函数可以使用Reflection来构造应该用于该类型的方法的委托。如果尝试以违反其一般约束的方式构造泛型方法的委托,则此类调用将在运行时失败;因为静态构造函数中的exceptioins非常糟糕,所以应该确保使用有效函数构造一个委托。另一方面,一旦委托构建一次,所有未来的呼叫都可以直接通过该代表发送,而无需进一步反思或类型检查。