2009-08-08 3 views
6

私はがMessageRouterと呼ばれるクラスに入ってくるオブジェクトXMLシリアル化されたメッセージを持っています。 XMLには、それがシリアライズされた型名が含まれています。まで知られていない型に応じて、が異なるデリゲートメソッドを呼び出すことができる必要があります。その後、辞書<T>代表

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

とタイプを格納します。私はMessageRouterはそうのようなRegisterDelegateForType方法を提供したいと思います...ので、うまくいけば、これは誰かに意味をなすだろうジェネリックに

極めて強くありませんよ、またはこのような辞書では、型の文字列表現:

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. これができない場合は、これを行うにはどうすればよいでしょうか?

答えて

19

かなりの理由から、何らかの形で許可されていても、例の最後の行(デリゲートを取得してから呼び出すコード)は型保証されません。 Action<T> - Tを引数として呼び出しますが、それにはdeserializedObjectというobjectのものを渡します。キャストなしのプレーンコードでは機能しません。なぜあなたのケースの型チェックを回避できないと思われますか?最も単純なケースで

は、あなたがこのような何かを行うことができます。これは誰かが辞書に多かれ少なかれ以上の引数を取るデリゲートを追加することができ、かつますもちろん

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

あなただけよ実行時にDynamicInvokeコールで見つけてください。しかし、実際には、「任意のデリゲートですが、引数が1つしかない」という型を定義する方法はありません。このような

Dictionary<Type, Action<object>> registeredDelegates 

、その後、登録の種類:: - 少し冗長な場合 - のC#で

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

上記のスニペットは、C#3.0ラムダを使用していますが、あなたが同じことを行うことができ、より良いオプションは、このかもしれません2.0の匿名の代理人。今度はDynamicInvokeを使用する必要はありません。ラムダ自体が適切なキャストを行います。

最後に、ラムダの作成を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

これは完全にあなたの質問に答えているのか分かりませんが、ここではあなたが望むものを達成するために書いたクラスがあります。私はあなたがあなたのアクションの代理人が型付きオブジェクトを取るかどうかを知りたいとは言えませんでしたが、擬似コードではそれを "オブジェクト"に渡して非直列化を行い、それに従って私のクラスを書いたので、ジェネリックは使用しません:

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を辞書<タイプ、アクション>にしてみませんか? – thecoop

+0

routeNpingmeがデリゲート型のvoidをサポートする必要があるかどうかを確認するのを待っていたとします。(T t);そうであれば、Dictionary >を使用することはできませんでした。このデザインは簡単に修正することができます。そうでなければ、辞書<タイプ、アクション>が最適です。彼がAction をサポートする必要があるとしても、Dictionary を使用していれば、デザインはより良いでしょう。 – user152862