Here's sort of what I have (using Rhino Mocks, but that isn't central to the question):
var entityMock = MockRepository.GenerateMock<IEntity>();
this.Cache = MockRepository.GenerateStub<Cache<IEntity>>();
Is it possible to be more specific in setting the type parameter of Cache<T>
? Something like:
var entityMock = MockRepository.GenerateMock<IEntity>();
this.Cache = MockRepository.GenerateStub<Cache<typeof(entityMock)>>();
This doesn't compile of course. But I would like to, if possible, use the type that Rhino Mocks generates, which is a concrete implementation of IEntity
.
You can create a closed generic type at runtime using reflection. The problem is that you will most likely have to continue operating it using just reflection because (given that its type is not known at compile time) you cannot type it as something usable directly.
For example, to create a list of "something":
public IList CreateList(Type t)
{
var openListType = typeof(List<>);
return (IList)openListType.MakeGenericType(t);
}
This example illustrates a couple of important points:
CreateList
cannot accept t
as a generic type parameter and still allow the same functionality.object
. Here we know that we will always create an IList
, so things are a bit better.I don't have experience with Rhino Mocks, but in your case it would translate to this:
var entityMock = MockRepository.GenerateMock<IEntity>()
var cacheType = typeof(Cache<>).MakeGenericType(entityMock.GetType());
this.Cache = MockRepository.GenerateStub(cacheType);
...but only if an appropriate GenerateStub
overload is available.
Note that although RhinoMocks is mentioned, the only specific part is GenerateStub
which creates a concrete implementation around the provided type; the rest is agnostic of RhinoMocks.
Generic type arguments are resolved at compile time - as you know; this disallows entering the type as a generic argument in-line without reflection (ie: var list = new List<typeof(int)>();
).
However, you can create generic types using reflection. In essense, if you can get the type of the dynamic proxy from something like:
var entityMock = MockRepository.GenerateMock<IEntity>();
var dynamicType = entityMock.GetType();
MockRepository
has a GenerateStub
that takes a Type
and object[]
arguments, so carrying on from above:
var cacheType = typeof(Cache<>);
var genericType = cacheType.MakeGenericType(dynamicType);
var stubbed = MockRepository.GenerateStub(genericType, null);
The stubbed
item is unfortunately of type object
, but as Lucero states in the comments, it would be possible to use generic co-variance to gain a type more usable than just object
. I demonstrate this below.
ICache<out T>
interface to represent the Cache, the generic co-variance will then allow you to cast the resulting proxies to the base type (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; }
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With