Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
330 views
in Technique[技术] by (71.8m points)

c# - Dictionary of Action<T> Delegates

I have object XML serialized messages coming into a class called MessageRouter. The XML contains the Type name it it was serialized from, and I need to be able to invoke different delegate methods depending on the type that are not known until runtime. I'm not extremely strong in generics so hopefully this will make sense to someone...

I'd like MessageRouter to provide a RegisterDelegateForType method like so:

myMessageRouter.RegisterDelegateForType(new Action<MySerializableType>(myActionHandler));

And then store the types, or the type's string representation in a Dictionary like this:

Dictionary<Type, Action<T>> registeredDelegates;

That way, I can do something like the following pseudocode, calling the type's assigned delegate and passing the deserialized object:

Type xmlSerializedType = TypeFromXmlString(incomingXml);
object deserializedObject = DeserializeObjectFromXml(xmlSerializedType, incomingXml);

// then invoke the action and pass in the deserialized object
registeredDelegates[xmlSerializedType](deserializedObject);

So my questions are:

  1. How do you define a Dictionary that can contain a Type as a key and a generic Action<T> as a value, and have the RegisterDelegateForType method populate the dictionary?
  2. If that's not possible, what's the best way to do this?
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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).


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...