Как я могу реализовать обработчики событий с DynamicObject в C # - PullRequest
3 голосов
/ 24 ноября 2010

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

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

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

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

Есть какие-нибудь подсказки?

Ответы [ 3 ]

2 голосов
/ 24 ноября 2010

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

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 голосов
/ 24 ноября 2010

Я думаю, что вы путаете события с делегатами.События по сути являются делегатами, но вы не можете использовать методы доступа «добавить» и «удалить» с делегатами - однако + = и - = работают одинаково с обоими.

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 не будут работать правильно.

0 голосов
/ 24 ноября 2010

Вызовите 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();

Вывод: ДЕЛАЙТЕ что-нибудь!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...