Как вызвать события родительского класса с помощью Expression (C #) - PullRequest
1 голос
/ 03 декабря 2010

Я пишу библиотеку классов для веб-API.

У меня есть базовый класс и интерфейс с 30 блоками:

interface ISomethingApi {
    void AuthenticateAsync(string username, string password);
    event AsyncResponseHandler AuthenticateEnded;

    void GetMemberAsync(string username);
    event AsyncResponseHandler<Member> GetMemberEnded;

    // more...
}

Базовый класс BaseHttpClient содержит реализацию, и все методы являются пустыми и виртуальными.

class BaseHttpClient : ISomethingApi {

    public virtual void GetMemberAsync(string username) {
        throw new NotImplementedException();
    }

    public event AsyncResponseHandler<Member> GetMemberEnded;

    // more...

}

Поскольку API довольно нестандартный, я наследую базовый класс с классом XmlClient. Этот класс переопределяет виртуальные методы и выполняет работу.

class XmlClient : BaseHttpClient {
    public override void GetMemberAsync(string username) {
        Member member;
        // process here

        // raising the event
        GetMemberEnded(this, new AsyncResponseArgs<Member>(member));
        // error: LogoffEnded can only appear on the left hand side of += or -=
    }
}

Проблема в том, что я не могу поднять события:

Событие BaseHttpClient.LogoffEnded может появляться только в левой части + = или - =

Основным решением является создание методов в базовом классе, таких как

protected void RaiseLogoffEnded(AsyncResponseArgs args) {
    if (LogoffEnded != null) {
        LogoffEnded(this, args);
    }
}

Но существует слишком много способов для создания. Я хотел бы сделать что-то вроде:

public override void GetMemberAsync(string username) {
    Member member;
    // work done here

    RaiseEvent(x => x.GetMemberEnded, new AsyncResponseArgs<Member>(member));
}

Полагаю, речь идет об отражении и выражениях.

  • Это правильный способ сделать? (Performace)
  • Какую документацию я мог бы прочитать, чтобы сделать это?
  • Не могли бы вы показать мне действительный код для этого?

Ответы [ 4 ]

2 голосов
/ 03 декабря 2010

Вы можете использовать несколько статических методов расширения:

static class Extensions
{
    public static void Raise(this EventHandler @event, object sender, EventArgs e)
    {
        if (@event != null)
            @event(sender, e);
    }

    public static void Raise<T>(this EventHandler<T> @event, object sender, T e) where T : EventArgs
    {
        if (@event != null)
            @event(sender, e);
    }
}

, где вы могли бы сделать:

public class MyClass
{
    public event EventHandler MyEvent;

    public void DoSomething()
    {
        MyEvent.Raise(this, EventArgs.Empty);   
    }
}

Хотя на самом деле вы можете использовать выражение, например ::10000

public void Raise<T>(Expression<Func<EventHandler<T>>> expr, T eventArgs)
    where T : EventArgs
{
    EventHandler<T> handler = expr.Compile().Invoke();
    handler(this, eventArgs);
}

Вы, вероятно, хотите покончить с избыточным выражением и просто использовать вместо него Func<T>, поскольку вы вызываете событие из класса напрямую. С помощью выражений вам нужно будет скомпилировать выражение, тогда как Func<T> вы этого не сделаете:

public void Raise<T>(Func<EventHandler<T>> func, T eventArgs)
    where T : EventArgs
{
    EventHandler<T> handler = func();
    handler(this, eventArgs);
}
1 голос
/ 04 декабря 2010

Вы должны переместить ваши методы RaiseXXX в родительский класс, где у вас есть определенные события.Убедитесь, что эти методы как минимум защищены.

И не забудьте вызвать ваши события через локальную переменную, чтобы минимизировать поле ошибки.

var e = MyEvent;

if(e! = null) e (this, EventArgs.Empty);

1 голос
/ 04 декабря 2010

Вы можете использовать System.ComponentModel.EventHandlerList, что даст вам два преимущества:

1) У вас будет механизм FireEvent.

2) Участник Events не использует память, если на него не подписаны делегаты. Если у вас есть класс с 30 событиями, в вашем классе будет 30 указателей, независимо от того, есть ли подписчики или нет. EventHandlerList - это отдельный объект, который содержит все подписанные делегаты. Это очень легкая карта (не словарь). Обратите внимание на то, что ключи событий являются статическими объектами, чтобы не добавлять в зону присутствия класса.

class AsyncResponseArgs : EventArgs
{
    public Member Member { get; private set; }
    public AsyncResponseArgs(Member m)
    {
        Member = m;
    }
}

interface ISomethingApi
{
    void AuthenticateAsync(string username, string password);
    event EventHandler<AsyncResponseArgs> AuthenticateEnded;

    void GetMemberAsync(string username);
    event EventHandler<AsyncResponseArgs> GetMemberEnded;
}

class BaseHttpClient : ISomethingApi
{
    private EventHandlerList Events = new EventHandlerList();

    public virtual void AuthenticateAsync(string username, string password)
    {
        throw new NotImplementedException();
    }
    protected static object AuthenticateEndedEvent = new object();
    public event EventHandler<AsyncResponseArgs> AuthenticateEnded
    {
        add { Events.AddHandler(AuthenticateEndedEvent, value); }
        remove { Events.RemoveHandler(AuthenticateEndedEvent, value); }
    }

    public virtual void GetMemberAsync(string username)
    {
        throw new NotImplementedException();
    }
    protected static object GetMemberEndedEvent = new object();
    public event EventHandler<AsyncResponseArgs> GetMemberEnded
    {
        add { Events.AddHandler(GetMemberEndedEvent, value); }
        remove { Events.RemoveHandler(GetMemberEndedEvent, value); }
    }

    protected void FireEvent(object key, AsyncResponseArgs e)
    {
        EventHandler<AsyncResponseArgs> handler = (EventHandler<AsyncResponseArgs>)Events[key];
        if (handler != null)
            handler(this, e);
    }
}

class XmlClient : BaseHttpClient
{
    public override void GetMemberAsync(string username)
    {
        Member member;
        // process here 
        FireEvent(GetMemberEndedEvent, new AsyncResponseArgs(member));
    }
}

Добавлено:

Вы можете сэкономить некоторые печатные данные в BaseHttpClient, написав фрагмент кода .

0 голосов
/ 06 декабря 2010

Вы можете добавить метод к базовому классу, который принимает имя события в виде строки и вызывает соответствующее событие с помощью отражения, например

public void Raise(String eventName, object source, EventArgs eventArgs)
{
    var field = this.GetType().GetField(eventName, BindingFlags.Instance | BindingFlags.NonPublic);
    if (field == null)
        throw new ArgumentException("No such event: " + eventName);

    var eventDelegate = (MulticastDelegate)field.GetValue(this);
    if (eventDelegate != null)
        foreach (var handler in eventDelegate.GetInvocationList())
            handler.Method.Invoke(handler.Target, new object[] { source, eventArgs });                
}

Хотя я ничего не знаю о производительности.

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