Partially closed generic types
If you swallow enough of the generic pills, you may run into situations where a not-quite closed and a not-quite open generic type would be nice. It’s in situations where decisions based on types are prevalent, such as in IoC containers. An open generic type is simply a generic type whose type parameters have not been specified. For example, IEnumerable<> is an open generic type, and IEnumerable
Recently I ran into a situation where I wanted my cake and eat it too, with a partially closed generic type. First, I had a generic type I wanted to work with:
public interface IRequestHandler<TMessage> { void Handle(TMessage message); }
Nothing too exciting here, except I wanted to match up, using an IoC container, TMessage to handlers of that message (IRequestHandler
cfg.ConnectImplementationsToTypesClosing(typeof(IRequestHandler<>));
This one line of code connects all implementations of IRequestHandler<> to the closed interfaces. For example, the closed implementation AddCustomerHandler gets connected to IRequestHandler
public interface IDeleteMessage<TEntity> where TEntity : Entity { TEntity Entity { get; } }
The handler could handle any delete message, by supplying an additional generic parameter:
public class DeleteRequestHandler<TEntity> : IRequestHandler<IDeleteMessage<TEntity>> where TEntity : Entity { private readonly IRepository<TEntity> _repository; public DeleteRequestHandler(IRepository<TEntity> repository) { _repository = repository; } public void Handle(IDeleteMessage<TEntity> message) { // delete the message.Entity } }
So this DeleteRequestHandler could handle all sorts of delete messages, so long as it was an IDeleteMessage
Partially closed weirdness
This led me to want to do this:
[Test] public void Wont_compile() { var type = typeof (IRequestHandler<IDeleteMessage<>>); }
However, I got a compile error of:
error CS1031: Type expected
So the compiler does not let me close a generic type with another open generic type. I want to define the type “IRequestHandler of IDeleteMessage of IDontCareRightNowJustYet”, maybe I can do this with type objects directly? Here’s what I had in that vein:
[Test] public void Compiles_but_useless() { var openRequestType = typeof (IRequestHandler<>); var openMessageType = typeof (IDeleteMessage<>); var partiallyClosedType = openRequestType.MakeGenericType(openMessageType); Debug.WriteLine(partiallyClosedType); }
The really interesting output is the name of the type:
PartiallyClosedGenerics.IRequestHandler`1[PartiallyClosedGenerics.IDeleteMessage`1[TEntity]]
It looks like a partially closed type, as you can see that IRequestHandler is closed type, but closed with an open generic type. Unfortunately, you can’t actually do anything with this type, as Type.IsGenericTypeDefinition is false, meaning I can’t create a completely closed type. Calling MakeGenericType with something like typeof(Customer) doesn’t work, and you can’t really poke around the generic type parameter to try and coerce it. So although you can create partially closed generic Type objects, you can’t do anything with them. Activator.CreateInstance fails, for example.
This is because a partially closed type is a fully open type. If a type contains any unassigned type parameters, anywhere in its hierarchy, the type is an open constructed type. Unfortunately, MakeGenericType only works with types that are generic type definitions, so I’ve created a descriptive, but useless type. More information than I ever, ever cared to know about generics, but good to know that the CLR team really covered their bases when they implemented generics.