2010-11-24 26 views
3

C#のDynamicObjectを使用してQtのクラスシステム用の汎用Wrapper-Classを実装しようとしています。しかし、私は次のコードを書きたい:上記C#でDynamicObjectを使用してイベントアクセッサを実装する方法

dynamic obj = new SomeWrapperClass(....); // This extends DynamicObject 
obj.OnMyEvent += (Action)(() => Console.WriteLine("DO something!")); 

を(アクションへの明示的なキャストが必要です)VS2010に応じて有効なコードですが、どのように正確に行う私は「キャッチ」DynamicObjectのメソッドを使用して、その文?

TryGetMember()を実装しようとしましたが、ステートメントが呼び出されましたが、動作させるために何を返す必要があるかわかりません。

ヒント

答えて

2

リフレクターは、この1つにあなたの友人です。

if(Binder.IsEvent("OnMyEvent", typeof(SomeWrapperClass))) 
{ 
    Binder.InvokeMember("add_OnMyEvent", obj, myAction); 
} 
else 
{ 
    var e = Binder.GetMember("OnMyEvent", obj); 
    var ae = Binder.BinaryOperation(ExpressionType.AddAssign, e, myAction); 
    Binder.SetMember("OnMyEvent", obj, ae); 
} 

あなたは(あなたがデフォルトDynamicObject実装に傾くことができ、その場合には)OnMyEventのための本当のイベントを使用できない場合、あなた」:2番目の行のために生成されたコードは(約)は、このようになりますAddAssignがマルチキャストデリゲートのようなものを返すようなものを返す必要があります。

public class SomeWrapperClass : DynamicObject 
{ 
    public event Action OnMyOtherEvent; 

    public override bool TryGetMember(GetMemberBinder binder, out object result) 
    { 
     if (binder.Name == "OnMyEvent") 
     { 
      result = OnMyOtherEvent; 
      return true; 
     } 
     return base.TryGetMember(binder, out result); 
    } 

    public override bool TrySetMember(SetMemberBinder binder, object value) 
    { 
     if (binder.Name == "OnMyEvent" && value is Action) 
     { 
      OnMyOtherEvent = (Action)value; 
      return true; 
     } 
     return TrySetMember(binder, value); 
    } 

    public void Test() 
    { 
     if (OnMyOtherEvent != null) 
      OnMyOtherEvent(); 
    } 

    private static void TestEventHandling() 
    { 
     dynamic obj = new SomeWrapperClass(); // This extends DynamicObject 
     obj.OnMyEvent += (Action)(() => Console.WriteLine("DO something!")); 
     obj.Test(); 
    } 
} 
+0

最初に、生成したILをちょうど見ることについてのヒントに感謝します(よくまたはかなり高いレベルの表現)。かなり興味深いのは、GetMemberの戻り値を処理する方法です。 TryGetMemberから返されたすべてのイベントに対して静的な「模擬」イベントを使用してTryInvokeMemberで実際のバインディングを実行するだけではどうですか? (add_XYZEventの場合)? – Storm

+0

それは私が試みることです、はい。私はそれがうまくいくかどうか分かりませんが、もしそれが分かれば分かち合いましょう。 :) – dahlbyk

0

が反射して、あなたのActionを呼び出します:

dynamic o = new SomeWrapperClass(); 
o.OnMyEvent += (Action)(() => Console.WriteLine("DO something!")); 
var a = typeof(SomeWrapperClass).GetField("OnMyEvent", BindingFlags.Instance | BindingFlags.NonPublic); 
(a.GetValue(o) as Action).Invoke(); 

私は楽しみのために

...可能ならば、かつてお勧めしたい、ここで動的OnMyOtherEventにOnMyEventをバインドハック例です。出力: DO何か!

0

私はあなたが代議員とイベントを混同していると思います。イベントは事実上デリゲートですが、デリゲートで 'add'と 'remove'アクセサを使用することはできませんが、+ =と - =は両方とも同じように動作します。

obj.OnMyEvent += (Action)(() => Console.WriteLine("DO something!")); 

そのデリゲートは、類似したタイプ(この場合はパラメータのないアクションデリゲートを)持っている必要がありますので、これは基本的には、デリゲートの呼び出しリストにターゲットを追加しています。

提案実装は以下の通りです:あなたはアクションデリゲートで空のアクションを必要とする

private Action actiondelegate = (Action)(() => {}); 

public override bool TryGetMember(GetMemberBinder binder, out object result) 
{ 
    if (binder.Name == "OnMyEvent") 
    { 
     result = actiondelegate; 
     return true; 
    } 
} 

注 - それは正しくヌルTryGetMemberTrySetMember文句を言わない仕事である場合ためです。

関連する問題