Strongly typed way of storing type references
I need to store a collection of types.
All types implement the same interface IHandler<T>
, where T
is a parameter of the parent class.
At runtime, I enumerate the list of handlers and process a message. Each handler is created by a builder (just uses StructureMap internally). The builder exposes a method like:
static void DoSomething<T>(Action<T> action)
{
}
Of course, I only have a Type
so can't use the above.
I've got round this by also passing the underlying interface as the generic parameter and the concrete type as a parameter:
DoSomething<IHandler<T>>(handlerType, h =>
{
h.Handle(message);
});
Then inside DoSomething
I can get an instance of handlerType
but cast it as IHandler<T>
.
Just wondered if there was a better/cleaner way.
Update
In response to some of the comments.
The collection is an ICollection<Type>
, not instances. The message handlers are created on demand, on different threads, for each batch of messages, so creating the handlers in advance, or using Lazy<T>
, was not an option.
Essentially I am trying to abstract away some direct references to StructureMap. Specifically, DoSomething<T>
actually creates the handler using a nested container, before executing the action (it's Handle
method).
Update 2 (solution)
I realized that I could handle (no pun intended) this better by storing a collection of Action<T>
and creating the handlers using a factory. Here's a simple example:
public class SimpleProcessor<T> where T : IMessage
{
ICollection<Action<T>> ha开发者_运维知识库ndlers;
T message;
public SimpleProcessor(T message)
{
this.handlers = new List<Action<T>>();
this.message = message;
}
public void AddHandler(Action<T> handler)
{
handlers.Add(handler);
}
public void Process()
{
foreach (var handler in handlers)
{
handler(message);
}
}
}
Usage:
var testMessage = new TestMessage { Message = "Foo" };
var simpleProcessor = new SimpleProcessor<TestMessage>(testMessage);
simpleProcessor.AddHandler(m => DoSomething<TestMessageHandler>(h => h.Handle(m)));
simpleProcessor.Process();
I'm more or less happy with this solution.
If you're willing to change Action<T>
into Action<dynamic>
, then you can do something like this:
class Program
{
static void Main(string[] args)
{
try
{
var myMessage = new object();
Action<dynamic> action = (dynamic h) => { h.Handle(myMessage); };
Type myType = typeof(int);
var method = typeof(Program).GetMethod("DoSomething");
var concreteMethod = method.MakeGenericMethod(myType);
concreteMethod.Invoke(null, new [] { action });
Console.ReadKey();
}
catch (Exception ex)
{
Console.Error.WriteLine(ex);
Console.ReadKey();
}
}
static public void DoSomething<T>(Action<dynamic> action)
{
Console.WriteLine("DoSomething invoked with T = " + typeof(T).FullName);
}
}
精彩评论