Добавление действия к аргументам обработчика событий c # - PullRequest
0 голосов
/ 09 сентября 2011

У меня проблема с некоторым кодом, который мне нужен для рефакторинга. Прямо сейчас он использует лямбды в качестве обработчиков событий, но они не удаляются должным образом. Из того, что я прочитал, это даже невозможно? В любом случае, я хотел бы переписать его, чтобы использовать делегат вместо анонимной функции, и теперь моя проблема в том, что прямо сейчас он принимает действие в качестве параметра, и я не могу понять, как передать действие моему новому делегировать. Это код:

void RetrieveData(
            int pointId,
            int? chartCollectionId,
            Action action)
        {
            if (pointId <= 0)
                throw new ArgumentException("PointId not valid");

            LastPointId = NextPointId;
            NextPointId = pointId;

            Clear();

            _csr = new CustomerServiceRepository();

            _csr.ServiceClient.GetChartDataCompleted += (se, ea) =>
                                                            {
                                                                _cachedCharts = ea.Result;
                                                                ChartDataRetrieved(ea.Result);
                                                                if (action != null)
                                                                    action.Invoke();
                                                                _csr = null;
                                                            };
            _csr.ServiceClient.GetChartDataAsync(
                Settings.Current.Customer.CustomerName,
                pointId,
                chartCollectionId);

            _csr.ServiceClient.GetChartDataCompleted -= (se, ea) => //remove after usage
                                                            {
                                                                _cachedCharts = ea.Result;
                                                                ChartDataRetrieved(ea.Result);
                                                                if (action != null)
                                                                    action.Invoke();
                                                                _csr = null;
                                                            };
        }

Я думал, что, возможно, смогу создать следующее:

public class extendedEventArgs : GetChartDataCompletedEventArgs
        {
            Action foo { get; set; }
        }

        void tang(object sender, extendedEventArgs e)
        {
            _cachedCharts = e.Result;
            ChartDataRetrieved(e.Result);
            if (action != null)
                action.Invoke();
            _csr = null;
        }

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

_csr.ServiceClient.GetChartDataCompleted += new EventHandler<extendedEventHandler>(tang);

выдает ошибку:

Cannot implicitly convert type  System.EventHandler<Conwx.Net.Client.CustomerClient.Controls.ChartControls.ChartListForecast.extendedEventArgs>'  to  System.EventHandler<Conwx.Net.Client.Framework.CustomerServiceReference.GetChartDataCompletedEventArgs>'

Что я здесь не так делаю? Альтернативные решения также приветствуются.

Ответы [ 3 ]

1 голос
/ 09 сентября 2011

Насколько я понимаю, ключевая проблема здесь заключается в невозможности удалить обработчик; если это так, все, что вам нужно для хранения делегата (где в приведенном ниже значении YourDelegateType означает: определенный тип GetChartDataCompleted):

YourDelegateType handler = (se, ea) =>
    {
        _cachedCharts = ea.Result;
        ChartDataRetrieved(ea.Result);
        if (action != null)
            action.Invoke();
        _csr = null;
    };
_csr.ServiceClient.GetChartDataCompleted += handler;
...
_csr.ServiceClient.GetChartDataCompleted -= handler;

Вы также можете отключить отписку (например, чтобы отписываться при возникновении события):

YourDelegateType handler = null;
handler = (se, ea) =>
    {
        _cachedCharts = ea.Result;
        ChartDataRetrieved(ea.Result);
        if (action != null)
            action.Invoke();
        _csr.ServiceClient.GetChartDataCompleted -= handler;
        _csr = null;
    };
_csr.ServiceClient.GetChartDataCompleted += handler;
1 голос
/ 09 сентября 2011

Нет, вы не можете сделать это, потому что это класс, который вызывает событие GetChartDataCompleted, которое создает объект, переданный (в качестве ссылки) обработчику события. Он будет создавать GetChartDataCompletedEventArgs, а не extendedEventArgs.

Если подумать, это похоже на попытку реализовать интерфейс, который выглядит следующим образом:

public interface IFoo
{
    void Foo(object x);
}

с таким классом:

public class Bar : IFoo
{
    // We don't care if someone calling IFoo wants to pass us something
    // other than a string - we want a string, darn it!
    public void Foo(string y)
    {
        Console.WriteLine(y.Length);
    }
}

Это явно не сработает ...

Марк показал один из подходов к его исправлению, но я также хотел бы отметить, что вы, вероятно, на самом деле должны удалять делегата только при возникновении события . Я предполагаю, что тот факт, что метод называется GetChartDataAsync, означает, что это неблокирующий метод ... поэтому отмена подписки на событие сразу после его вызова, вероятно, не очень хорошая идея.

0 голосов
/ 09 сентября 2011

Если вы предпочитаете избегать анонимных методов, вы можете вручную сделать то, что компилятор делает для вас изнутри.Таким образом, создайте класс замыкания, который будет содержать Action и ссылку на себя как поля и который предоставляет метод, который вы хотите назначить событию.Примерно так:

class RetrieveDataClosure
{
   private Action action;
   private MyClass self;

   public RetrieveDataClosure(Action action, MyClass self)
   {
      this.action = action;
      this.self = self;
   }

   public void ChartDataCompleted(object se, MyEventArgs ea)
   {
      self._cachedCharts = ea.Result; 
      self.ChartDataRetrieved(ea.Result); 
      if (action != null) 
         action.Invoke(); 
      self._csr = null; 
   }
}

Что бы вы использовали в своем коде так:

var closure = new RetrieveDataClosure(action, this);
_csr = new CustomerServiceRepository();
_csr.ServiceClient.GetChartDataCompleted += closure.ChartDataCompleted;
_csr.ServiceClient.GetChartDataAsync( 
   Settings.Current.Customer.CustomerName, 
   pointId, 
   chartCollectionId); 
_csr.ServiceClient.GetChartDataCompleted -= closure.ChartDataCompleted;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...