2010-11-24 2 views
3

Я пытаюсь реализовать общий класс класса Wrapper-класса для Qt с использованием DynamicObject C#. Тем не менее, я хочу, чтобы написать следующий код:Как я могу реализовать event acessors с DynamicObject в C#

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

выше действует код в соответствии с VS2010 (явное приведение к действию требуется), но как именно я «поймать» это заявление, используя методы DynamicObject в?

Я попытался реализовать TryGetMember(), и он вызван для утверждения, но я понятия не имею, что мне нужно вернуть, чтобы заставить его работать.

Любые подсказки?

ответ

2

Отражатель - ваш друг на этом. Код, сгенерированный для второй линии выглядит примерно так (примерно):

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

Если вы не можете использовать реальное событие для OnMyEvent (в этом случае вы можете опереться на реализацию по умолчанию DynamicObject), то вы» Мне нужно вернуть что-то, что реализует AddAssign, возвращая что-то вроде делегата многоадресной передачи. Я хотел бы предложить бывший, если это возможно ...

Для удовольствия, вот хак примера, который динамически связывает OnMyEvent с OnMyOtherEvent:

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

Прежде всего, спасибо LOT за подсказку о том, как просто смотреть на IL, который он генерирует (ну или довольно высокоуровневое представление). Я считаю довольно интересным то, как он обрабатывает возвращаемое значение GetMember. Не могу ли я использовать статическое событие «mock» для возврата из TryGetMember для всех моих событий, а затем выполнить фактическое привязку в TryInvokeMember? (для add_XYZEvent)? – Storm 2010-11-24 17:44:48

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

Выход: Что-то!

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

Обратите внимание, что вам нужно создать пустое действие в своем действии делегата - это потому, что если обнулить не будет работать TryGetMember и TrySetMember правильно.

Смежные вопросы