动作词典< T>代表 [英] Dictionary of Action<T> Delegates

查看:89
本文介绍了动作词典< T>代表的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个名为 MessageRouter 的对象 XML序列化消息。 XML包含它被序列化的类型名称,并且我需要能够根据直到运行时才知道的类型调用不同的委托方法。我不是非常强大的泛型,所以希望这对别人有意义...



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

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

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

  Dictionary< Type,Action< T>> registeredDelegates; 

这样,我可以做下面的伪代码,调用类型的赋值委托并传递反序列化object:

 类型xmlSerializedType = TypeFromXmlString(incomingXml); 
object deserializedObject = DeserializeObjectFromXml(xmlSerializedType,incomingXml);

//然后调用动作并传入反序列化的对象
registeredDelegates [xmlSerializedType](deserializedObject);

所以我的问题是:


  1. 如何定义一个可以包含 Type 作为键和通用 Action< /> 作为一个值,并有RegisterDelegateForType方法填充字典?

  2. 如果这是不可能的,那么做到这一点的最好方法是什么?

  3. ol>

    解决方案

    你不能这么做,因为很明显的原因 - 即使某种程度上允许,你的例子中的最后一行代码其中一个检索委托然后调用它)将是非类型安全的,因为您正在调用 Action< T> - 这要求 T 作为参数 - 并传递它< deserializedObject ,它的类型是 object 。如果没有强制转换,它将无法用普通的代码工作,为什么你会希望能够绕过你的情况的类型检查?



    在最简单的情况下,你可以做这样的事情:

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

    当然这会允许某人添加一个委托,它可以为字典提供多于或少于一个参数,你只能在运行时找到 DynamicInvoke 调用。但是没有什么办法可以定义一种说任何代表,但只有一个参数的类型。一个更好的选择可能是这样的:

     字典< 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; 

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

    现在调用者可以这样做:

      RegisterDelegateForType< MySerializableType>(myHandler)



    <因此它对您的客户来说是完全类型安全的。当然,你仍然有责任做正确的事情(例如,将正确类型的对象传递给从字典中检索的委托)。


    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?

    解决方案

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

    这篇关于动作词典&lt; T&gt;代表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆