Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to keep a list of open generic types in one place?

I'm trying to have a List of open generic types. Is it possible to have something like:

public class MessageProcessor
{
    private IDictionary<Type, IMessageHandler<>> _messageHandlers 
        = new Dictionary<Type, IMessageHandler<>>();

    public void AddHandler<TMessage>(IMessageHandler<TMessage> handler)
    {
        var messageType = typeof(TMessage);
        // Add to dictionary here
    }

    public void Handle<TMessage>(TMessage message)
    {
        // Call the correct handler here.
    }
}

IMessageHandler should have a strongly typed method:

public void Handle(TMessage message) {}

My real example is a little more complex, so I hope I've simplified it correctly here.

The fact is, I'm not interested in what the generic type is of each handler. I just need all of them in one place, and I can easily find the correct handler, if I can get them in that one place.

The private Dictionary will have the Type of the message (TMessage) as key. So I want to be able to do:

// ByteArrayHandler implements IMessageHandler<byte[]>
x.AddHandler(new ByteArrayHandler()) 
// StringHandler implements IMessageHandler<string>
x.AddHandler(new StringHandler()) 

x.Handle("Some message");
x.Handle(new byte[] { 1, 2, 3} );

And have the MessageProcessor call the correct MessageHandler.

like image 715
Peter Avatar asked Feb 13 '23 23:02

Peter


1 Answers

Everybody knows about extension methods. But what about "extension fields"? Of course it is impossible to extend some object with some new fields, but... have you seen ConditionalWeakTable class? Using that we can attach/associate some data to existing objects. The cool feature is that we don't need to explicitly remove the items from that dictionary. The objects are stored as weak references so when the key is collected by GC the key-value pair is automatically removed. Using that we could invent this tricky solution:

public class MessageProcessor
{
    private static class MessageHandlerHolder<TMessage>
    {
        public static readonly ConditionalWeakTable<MessageProcessor, IMessageHandler<TMessage>> MessageHandlers =
            new ConditionalWeakTable<MessageProcessor, IMessageHandler<TMessage>>();
    }

    public void AddHandler<TMessage>(IMessageHandler<TMessage> handler)
    {
        MessageHandlerHolder<TMessage>.MessageHandlers.Add(this, handler);
    }

    public void Handle<TMessage>(TMessage message)
    {
        IMessageHandler<TMessage> handler;
        if (!MessageHandlerHolder<TMessage>.MessageHandlers.TryGetValue(this, out handler))
            throw new InvalidOperationException("...");
        handler.Handle(message);
    }
}

So everything is strongly and statically typed and clients don't need to explicitly remove the handlers to avoid memory leaks.

like image 141
artelk Avatar answered May 24 '23 22:05

artelk