请注意,虽然提到了 RhinoMocks,但唯一具体的部分是GenerateStub
围绕提供的类型创建具体实现;其余的与 RhinoMocks 无关。
泛型类型参数在编译时解析 - 如您所知;这不允许在没有反射的情况下将类型作为通用参数内联输入(即:)var list = new List<typeof(int)>();
。
但是,您可以使用反射创建泛型类型。从本质上讲,如果您可以通过以下方式获取动态代理的类型:
var entityMock = MockRepository.GenerateMock<IEntity>();
var dynamicType = entityMock.GetType();
MockRepository
有 aGenerateStub
需要 aType
和object[]
参数,所以从上面继续:
var cacheType = typeof(Cache<>);
var genericType = cacheType.MakeGenericType(dynamicType);
var stubbed = MockRepository.GenerateStub(genericType, null);
不幸的是,该stubbed
项目是 type object
,但正如 Lucero 在评论中所说的那样,可以使用通用协方差来获得比 更有用的类型object
。我在下面演示这一点。
根据与 Lucero 的有趣讨论,如果您定义一个新
ICache<out T>
接口来表示 Cache,则通用协方差将允许您将生成的代理转换为基本类型 (of
ICache<IEntity>
):
class Program
{
static void Main(string[] args)
{
// The concrete entity.
IEntity entityMock = MockRepository.GenerateMock<IEntity>();
entityMock.Stub(s => s.Name).Return("Adam");
// The runtime type of the entity, this'll be typeof(a RhinoMocks proxy).
Type dynamicType = entityMock.GetType();
// Our open generic type.
Type cacheType = typeof(ICache<>);
// Get the generic type of ICache<DynamicProxyForIEntity> (our closed generic type).
Type genericType = cacheType.MakeGenericType(dynamicType);
// Concrete instance of ICache<DynamicProxyForIEntity>.
object stubbed = MockRepository.GenerateStub(genericType, null);
// Because of the generic co-variance in ICache<out T>, we can cast our
// dynamic concrete implementation down to a base representation
// (hint: try removing <out T> for <T> and it will compile, but not run).
ICache<IEntity> typedStub = (ICache<IEntity>)stubbed;
// Stub our interface with our concrete entity.
typedStub.Stub(s => s.Item).Return(entityMock);
Console.WriteLine(typedStub.Item.Name); // Prints "Adam".
Console.ReadLine();
}
}
public interface ICache<out T>
{
T Item { get; }
}
public interface IEntity
{
string Name { get; }
}