2009-08-08 16 views
6

tengo objeto XML mensajes serializados que entra en una clase llamada MessageRouter. El XML contiene el nombre de tipo desde el que se serialó, y necesito poder invocar diferentes métodos de delegado según el tipo que no se conocen hasta el momento de ejecución, . No soy muy fuerte en los genéricos y espero que esto tendrá sentido para alguien ...Diccionario de Acción <T> delegados

me gustaría MessageRouter para proporcionar una RegisterDelegateForType método de este modo:

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

Y luego almacenar los tipos o la representación del tipo de cadena en un diccionario como esto:

Dictionary<Type, Action<T>> registeredDelegates; 

de esa manera, se puede hacer algo como lo siguiente pseudocódigo, llamando delegado asignado al tipo y pasando el objeto deserializado:

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

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

Así que mis preguntas son:

  1. ¿Cómo se define un diccionario que puede contener un Type como una llave y un genérico Action<T> como un valor, y tienen el método RegisterDelegateForType pueblan el diccionario?
  2. Si eso no es posible, ¿cuál es la mejor manera de hacerlo?

Respuesta

19

No puede hacer esto como se describe, por razones bastante obvias, aunque de alguna manera permitida, la última línea de código en su ejemplo (la que recupera un delegado y la llama) sería no segura, como está llamando a Action<T> - que espera T como argumento - y aún así pasándolo deserializedObject, que es del tipo object. No funcionaría en código simple sin un molde, ¿por qué esperar poder eludir el cheque de tipo para su caso?

En el caso más simple, se puede hacer algo como esto:

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

Por supuesto que esto permitirá que alguien agregue un delegado que tiene más o menos de un argumento al diccionario, y usted sólo averiguar en la llamada DynamicInvoke, en tiempo de ejecución. Pero realmente no hay forma de definir un tipo que diga "cualquier delegado, pero con 1 argumento solamente". Una mejor opción podría ser la siguiente:

Dictionary<Type, Action<object>> registeredDelegates 

y luego los tipos de registro de la siguiente manera:

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

El fragmento anterior utiliza C# 3.0 lambdas, pero se puede hacer lo mismo - aunque un poco más detallado - con C# 2.0 delegados anónimos. Ahora no necesita usar DynamicInvoke - la lambda misma hará el molde apropiado.

Finalmente, puede encapsular la creación de lambda en RegisterDelegateForType convirtiéndolo en genérico.Por ejemplo:

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

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

Y ahora las personas que llaman pueden simplemente hacer:

RegisterDelegateForType<MySerializableType>(myHandler) 

Así es que es completamente typesafe para sus clientes. Por supuesto, usted sigue siendo responsable de hacerlo bien (es decir, pasar un objeto del tipo correcto al delegado que recupera del diccionario).

1

No estoy seguro de que esto responda completamente a su pregunta, pero esta es una clase que escribí que logrará lo que desea. No podría decir si desea que su delegado de Acción tome un objeto tipeado o no, pero en su pseudo código, lo pasa un "objeto" para deserializar, así que escribí mi clase en consecuencia y por lo tanto no usa genéricos:

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

Creo que esto logrará lo que desea.

+1

¿Por qué no hacer que IDictionary sea un diccionario ? – thecoop

+0

Supongo que estaba esperando a ver si routeNpingme necesitaría soportar un tipo de delegado de void Acción (T t); Si es así, no podría usar Diccionario > y este diseño podría modificarse fácilmente para respaldar eso. Si no, entonces sí, un Diccionario sería lo mejor. Incluso si necesita admitir la Acción , el diseño sería aún mejor si hubiera usado Diccionario . – user152862

Cuestiones relacionadas