行动词典代表

我有对象XML序列化消息进入一个名为MessageRouter的类。 XML包含它序列化的类型名称,我需要能够根据运行时之前未知的类型调用不同的委托方法 。 我在仿制药方面并不是非常强大,所以希望这对某些人有意义……

我希望MessageRouter提供一个RegisterDelegateForType方法,如下所示:

myMessageRouter.RegisterDelegateForType(new Action(myActionHandler)); 

然后将类型或类型的字符串表示forms存储在字典中,如下所示:

 Dictionary<Type, Action> registeredDelegates; 

这样,我可以执行类似下面的伪代码,调用类型的已分配委托并传递反序列化的对象:

 Type xmlSerializedType = TypeFromXmlString(incomingXml); object deserializedObject = DeserializeObjectFromXml(xmlSerializedType, incomingXml); // then invoke the action and pass in the deserialized object registeredDelegates[xmlSerializedType](deserializedObject); 

所以我的问题是:

  1. 如何定义一个可以包含Type作为键和一个通用Action作为值的Dictionary,并使用RegisterDelegateForType方法填充字典?
  2. 如果那是不可能的,那么最好的方法是什么?

你不能按照描述这样做,原因很明显 – 即使以某种方式允许,你的例子中的最后一行代码(检索委托然后调用它的那一行)也是非类型安全的,因为你正在调用一个Action – 期望T作为参数 – 然后传递deserializedObject ,它是object类型。 如果没有演员表单,它将无法在普通代码中工作,为什么您希望能够规避您的案例的类型检查?

在最简单的情况下,您可以执行以下操作:

 Dictionary registeredDelegates; ... registeredDelegates[xmlSerializedType].DynamicInvoke(deserializedObject); 

当然,这将允许某人添加一个委托,该委托对字典采用多于或少于一个参数,并且您只能在运行时找到DynamicInvoke调用。 但是实际上没有任何方法可以定义一个“任何委托,但只有1个参数”的类型。 一个更好的选择可能是:

 Dictionary> registeredDelegates 

然后注册这样的类型:

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

上面的代码片段使用的是C#3.0 lambdas,但你可以使用C#2.0匿名委托来做同样的事情 – 如果稍微冗长一点。 现在你不需要使用DynamicInvoke – lambda本身将进行适当的转换。

最后,您可以将lambda创建封装到RegisterDelegateForType本身,方法是将其设置为generics。 例如:

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

而现在呼叫者可以这样做:

 RegisterDelegateForType(myHandler) 

因此,它对您的客户来说是完全类型安全的。 当然,您仍然有责任正确地做到这一点(即将正确类型的对象传递给您从字典中检索的委托)。

我不确定这完全回答了你的问题,但是我写的这个课程将完成你想要的。 我无法判断你是否希望你的Action委托采用一个类型化的对象,但是在你的伪代码中,你传递一个“对象”来反序列化,所以我相应地编写了我的类,因此它不使用generics:

 public delegate void Action(object o); public class DelegateDictionary { private IDictionary _dictionary = new Hashtable(); public void Register(Action action) { _dictionary[typeof(T)] = action; } public Action Get() { 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(MyFunc); // Can be converted to an indexer so that you can use []'s var stringDelegate = dictionary.Get(); stringDelegate("Hello World"); } } 

我相信这会实现你想要的。