将委托转换为System.Action

时间:2013-11-08 06:48:14

标签: c# windows-phone-8 event-handling

简介

在我的 WP8 C#/ XAML项目中我正在使用事件通知我的视图已完成某些异步过程。

我有两种代表。

我正在从这些代表创建事件,并且有几个人通知我的观点,某些操作已完成或已启动(为了显示进度条,导航到另一个页面,禁用某些控件等)。

为了引发这些事件,我想创建一个私有的“通知方法”,这将提升这些代表。

我想到的方法在下面的代码示例中为fireUpEvent方法。

代码示例

ReturnState枚举

public enum ReturnState : int
{
  state1 = 0,
  ...            //for the purpose of the presentation
  state6 = 15
  ...
}

事件的定义&方法

public delegate void LoadingStartedEventHandler();
public delegate void LoadingFinishedEventHandler(ReturnState state);

public event LoadingStartedEventHandler LoadingStarted;
public event LoadingFinishedEventHandler LoadingFinished;

private void fireUpEvent(Action<ReturnState> action, Returnstate parameter)
{
  if(action != null)
  {
    action(parameter);
  }
}

private void fireUpEvent(Action action)
{
  if(action != null)
  {
    action();
  }
}

用法

fireUpEvent(LoadingFinished, ReturnState.state1);

描述

问题是,当我尝试编译时,我得到一个错误说:

Argument1: Cannot convert from 'XXXX.YYYY.SomeClass.LoadingFinishedEventHandler' to 'System.Action<XXXX.YYYY.Returnstate>'

我尝试使用谷歌搜索,但没有找到任何有用的东西。

为什么不能兑换?

我想在这些方法中Action<ReturnState>Action而不是特定的代表,是否可能?

我应该使用Action之类的任何其他“类型”吗?

我从这个“grooup”中知道的只有两个是Func&amp; Action,还有其他人吗?

2 个答案:

答案 0 :(得分:10)

回答这一行:

  

为什么不能兑换?

它们是不同的委托类型,并且在不同的委托类型之间没有引用转换(使用泛型方差的通用委托类型除外)。您可以像Action这样创建LoadingStartedEventHandler

LoadingStartedEventHandler handler = ...; // Whatever
Action action = new Action(handler);

...你可以反过来做同样的事情,并使用你的其他代表类型和Action<T>。但实际上没有必要这样做。

  

我想在这些方法中Action<ReturnState>Action而不是特定的代表,是否可能?

是的 - 只是不要使用这些代表声明事件,实际上根本没有声明代理人!

你可以改变这个:

public delegate void LoadingStartedEventHandler();
public delegate void LoadingFinishedEventHandler(ReturnState state);

public event LoadingStartedEventHandler LoadingStarted;
public event LoadingFinishedEventHandler LoadingFinished;

对此:

public event Action LoadingStarted;
public event Action<ReturnState> LoadingFinished;

请注意,这违反了.NET conventions on events,请注意。按照惯例,使用委托声明事件,其中第一个参数是类型为object的“发送者”,第二个参数是从EventArgs(或EventArgs本身派生的类型)。这对你来说是否重要是由你来决定的。如果您决定遵循约定,您基本上想要创建一个派生自EventArgs持有ReturnState的类型,然后将事件替换为:

public event EventHandler LoadingStarted;
public event EventHandler<ReturnStateEventArgs> LoadingFinished;

将帮助方法更改为:

private void RaiseEvent(EventHandler<TEventArgs> handler,
                        TEventArgs parameter)
{
  if(handler != null)
  {
    handler(this, parameter);
  }
}

private void RaiseEvent(EventHandler handler)
{
  if(handler != null)
  {
    handler(this, EventArgs.Empty);
  }
}

(我已经修改了方法名称以遵循.NET约定,并使用.NET术语来处理事件,这些事件是“引发”而不是“激发”。)

答案 1 :(得分:2)

先生。 Skeet总能得到很好的答案。在这里,我将提供您在问题中所述内容的另一种解决方案。

对于这两个:

  
      
  1. 为什么不能兑换?
  2.   
  3. 我想在那些方法中采取行动和行动,而不是特定的代表,是否可能?
  4.   

先生。 Skeet已经回答了。

对于这两个:

  
      
  1. 我应该使用像Action这样的其他“类型”吗?
  2.   
  3. 我从这个“grooup”中知道的只有两个是Func&amp;行动,还有其他人吗?
  4.   

您的fireUpEvent方法可能不应该接受一种简单的委托作为脆弱的设计。但它有可能做到。

C#中所有委托类型的最终基本类型是Delegate; Delegate的限制是它不能是泛型类型和方法中的where约束。

要回答这个问题(无论设计有什么关系),你可以说:

private void fireUpEvent(
        Delegate loadingEvent, ReturnState? parameter=null) {
    if(null!=loadingEvent) {
        foreach(var d in loadingEvent.GetInvocationList()) {
            var args=null!=parameter?new object[] { parameter }:null;
            d.Method.Invoke(d.Target, args);
        }
    }
}

而不是您问题中的原始两种方法,Target是拥有该方法的对象,如果它是静态方法,则会被视为thisnullargs是传递给方法的参数列表。

这只是一种你可以实现的方式,而且我很确定Skeet先生的答案绝对更好。