2010-11-24 7 views
3

Ich versuche, eine generische Wrapper-Klasse für Qt's Klassensystem mit C# 's DynamicObject zu implementieren. Allerdings mag ich den folgenden Code schreiben:Wie kann ich Event-Prozessoren mit DynamicObject in C# implementieren

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

Die oben ist gültige Code nach VS2010 (die explizite Umwandlung zum Handeln erforderlich ist), aber wie genau das tun, i „fängt“ diese Aussage Dynamic Methoden verwenden?

Ich habe versucht, TryGetMember() zu implementieren und es wird für die Anweisung aufgerufen, aber ich habe keine Ahnung, was ich zurückgeben muss, damit es funktioniert.

Irgendwelche Hinweise?

Antwort

2

Reflektor ist dein Freund auf diesem. Der Code generiert für Ihre zweite Zeile sieht etwa so aus (ungefähr):

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

Wenn Sie kein reales Ereignis für OnMyEvent verwenden können (in diesem Fall, dass Sie auf der Standard-DynamicObject Implementierung anlehnen kann), dann sind Sie‘ Ich muss etwas zurückgeben, das AddAssign implementiert, etwas wie ein Multicastdelegat zurückzugeben. Ich würde die ehemalige vorschlagen, wenn möglich ...

Für Spaß, hier ist ein hackish Beispiel, das dynamisch OnMyEvent zu OnMyOtherEvent bindet:

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

Zunächst einmal, vielen Dank für den Hinweis auf nur die IL (es oder eine eher High-Level-Darstellung) zu sehen. Was ich sehr interessant finde, ist, wie es den Rückgabewert von GetMember behandelt. Könnte ich nicht einfach ein statisches "Mock" -Ereignis verwenden, um von TryGetMember für alle meine Ereignisse zurückzukehren, und dann die tatsächliche Bindung in TryInvokeMember durchführen? (für add_XYZEvent)? – Storm

+0

Das würde ich versuchen, ja. Ich habe keine Ahnung, ob es gut funktionieren würde, aber wenn du es herausgefunden hast, teile es bitte mit. :) – dahlbyk

0

Invoke Ihre Action mit Reflexion:

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(); 

Ausgabe: TUN etwas!

0

Ich denke, Sie verwirren Ereignisse mit Delegierten. Ereignisse sind effektiv Delegaten, aber Sie können nicht die Accessoren 'Hinzufügen' und 'Entfernen' mit Delegaten verwenden - jedoch funktioniert das + = und - = bei beiden gleich.

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

Dies ist das Hinzufügen grundsätzlich ein Ziel der Aufrufliste eines Delegierten, so dass Delegat eine ähnliche Art (in diesem Fall ein parameterlos Aktionsdelegate) haben müssen.

Die vorgeschlagene Implementierung ist unten:

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

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

Beachten Sie, dass eine leere Aktion in Ihrem Aktionsdelegate müssen - dies, weil, wenn es die Null-TryGetMember und TrySetMember korrekt nicht funktionieren.

Verwandte Themen