为事件处理程序参数添加动作

本文关键字:添加 参数 事件处理 程序 | 更新日期: 2023-09-27 18:08:18

我有一些需要重构的代码问题。现在它使用lambdas作为事件处理程序,但它们没有被正确删除。据我所知,这根本不可能?无论如何,我想重写它,使用一个委托,而不是一个匿名函数,现在我的问题是,现在它把一个动作作为参数,我似乎不知道如何把动作传递给我的新委托。这是代码:

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>'

我在这里做错了什么?也欢迎其他解决方案。

。K

为事件处理程序参数添加动作

当我阅读它时,这里的关键问题是无法删除处理程序;如果是这样,您需要它来存储委托(在下面,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;

不,你不能这样做,因为它的类引发 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);
    }
}

这显然是行不通的…

Marc已经展示了一种修复它的方法——但我也要指出,你应该实际上只在事件触发时删除委托。我假设该方法被称为GetChartDataAsync意味着它是一个非阻塞方法…因此,在调用事件后立即取消订阅可能不是一个好主意。

如果您希望避免使用匿名方法,您可以手动完成编译器在底层为您完成的工作。也就是说,创建一个闭包类来保存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;