Dictionary of Action Delegates

后端 未结 2 900
滥情空心
滥情空心 2020-12-06 02:29

I have object XML serialized messages coming into a class called MessageRouter. The XML contains the Type name it it was serialized from, a

相关标签:
2条回答
  • 2020-12-06 02:33

    You cannot do this as described, for quite obvious reasons - even if somehow allowed, the last line of code in your example (the one which retrieves a delegate and then calls it) would be non-typesafe, as you're calling an Action<T> - which expects T as an argument - and yet passing it deserializedObject, which is of type object. It wouldn't work in plain code without a cast, why would you expect to be able to circumvent the type check for your case?

    In the simplest case, you can do something like this:

    Dictionary<Type, Delegate> registeredDelegates;
    ...
    registeredDelegates[xmlSerializedType].DynamicInvoke(deserializedObject);
    

    Of course this will allow someone to add a delegate which takes more or less than one argument to the dictionary, and you'll only find out at DynamicInvoke call, at run-time. But there isn't really any way to define a type which says "any delegate, but with 1 argument only". A better option might be this:

    Dictionary<Type, Action<object>> registeredDelegates
    

    and then registering types like this:

    myMessageRouter.RegisterDelegateForType<MySerializableType>(
       o => myActionHandler((MySerializableType)o)
    );
    

    The above snippet uses C# 3.0 lambdas, but you can do the same - if slightly more verbose - with C# 2.0 anonymous delegates. Now you don't need to use DynamicInvoke - the lambda itself will do the proper cast.

    Finally, you can encapsulate the lambda creation into RegisterDelegateForType itself by making it generic. For example:

    private Dictionary<Type, Action<object>> registeredDelegates;
    
    void RegisterDelegateForType<T>(Action<T> d)
    {
        registeredDelegates.Add(typeof(T), o => d((T)o));
    }
    

    And now the callers can just do:

    RegisterDelegateForType<MySerializableType>(myHandler)
    

    So it's completely typesafe for your clients. Of course, you're still responsible for doing it right (i.e. passing an object of the correct type to the delegate you retrieve from the dictionary).

    0 讨论(0)
  • 2020-12-06 02:52

    I am not sure that this completely answers your question, but here is a class I wrote that will accomplish what you want. I couldn't tell if you want your Action delegate to take a typed object or not, but in your pseudo code, you pass it an "object" to deserialize so I wrote my class accordingly and it therefore does not use generics:

    public delegate void Action(object o);
    
    public class DelegateDictionary {
        private IDictionary _dictionary = new Hashtable();
    
        public void Register<T>(Action action) {
            _dictionary[typeof(T)] = action;
        }
    
        public Action Get<T>() {
            return (Action)_dictionary[typeof(T)];
        }
    
        public static void MyFunc(object o) {
            Console.WriteLine(o.ToString());
        }
    
        public static void Run() {
            var dictionary = new DelegateDictionary();
            dictionary.Register<string>(MyFunc);
            // Can be converted to an indexer so that you can use []'s
            var stringDelegate = dictionary.Get<string>();
            stringDelegate("Hello World");
        }
    }
    

    I believe this will accomplish what you want.

    0 讨论(0)
提交回复
热议问题