动作<T>委托的字典

13

我有一个名为MessageRouter的类,接收XML序列化消息对象。XML中包含了它序列化自哪个类型,并且我需要在运行时根据类型调用不同的委托方法,但这些类型直到运行时才会确定,因此我需要实现动态调用。

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

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

然后将类型或类型的字符串表示存储在像这样的字典中:

Dictionary<Type, Action<T>> 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<T> 作为值,并使 RegisterDelegateForType 方法填充该字典?
  2. 如果不可能,最好的方法是什么?
2个回答

28
由于非常明显的原因,您无法按照描述的方式操作 - 即使以某种方式允许,您示例中的最后一行代码(检索委托并调用它的代码)也将无法进行类型安全检查,因为您正在调用一个期望参数为T的Action ,但传递了一个类型为object的deserializedObject。即使在普通代码中不经过强制转换也无法工作,您为什么希望能够绕过您的情况的类型检查呢?
在最简单的情况下,您可以执行以下操作:
Dictionary<Type, Delegate> registeredDelegates;
...
registeredDelegates[xmlSerializedType].DynamicInvoke(deserializedObject);

当然,这将允许某人向字典中添加一个委托,它可能接受多个或少于一个参数,在运行时您只能在DynamicInvoke调用时发现。但是实际上并没有一种方式来定义一种类型,它表示“任意委托,但仅限于1个参数”。更好的选择可能是这样:

Dictionary<Type, Action<object>> registeredDelegates

然后像这样注册类型:

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

上面的代码片段使用了C# 3.0的lambda表达式,但是您也可以使用C# 2.0的匿名委托进行相同的操作,只是语法会更冗长。现在您不需要使用DynamicInvoke,lambda表达式本身就能够进行适当的类型转换。

最后,您可以通过将其变为泛型来将lambda表达式的创建封装到RegisterDelegateForType中。例如:

private Dictionary<Type, Action<object>> registeredDelegates;

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

现在,调用者只需要执行以下操作:

RegisterDelegateForType<MySerializableType>(myHandler)

因此,对于您的客户来说,它完全是类型安全的。当然,您仍然需要负责正确地执行(即将正确类型的对象传递给从字典中检索到的委托)。


1

我不确定这完全回答了你的问题,但是这是我编写的一个类,它将实现你想要的功能。我无法确定你的 Action 委托是否需要使用类型化对象,但在你的伪代码中,你传递了一个“对象”来进行反序列化,所以我根据此编写了我的类,并且没有使用泛型:

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");
    }
}

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


1
为什么不将IDictionary改为Dictionary<Type, Action>呢? - thecoop
我想我一直在等待看看routeNpingme是否需要支持void Action<T>(T t)这样的委托类型。如果是这样,你就不能使用Dictionary<Type, Action<T>>,但这个设计很容易被修改以支持它。如果不是,那么Dictionary<Type, Action>将是最好的选择。即使他需要支持Action<T>,如果我使用了Dictionary<Type, object>,设计仍然会更好。 - user152862

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接