简介
在我的 WP8 C#/XAML 项目中,我使用事件来通知我的视图某些异步过程已完成。
我有两种类型的代表。
我正在从这些委托创建事件,其中有几个通知我的观点某些操作已完成或启动(为了显示进度条,导航到另一个页面,禁用一些控件等)。
为了引发这些事件,我想创建一个私有的"通知方法",这将引发这些委托。
我想到的方法在下面的代码示例中作为fireUpEvent
方法。
代码示例
返回状态枚举
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
吗?
我从这个"槽"中知道的唯一两个是Func
&Action
,还有其他人吗?
要回答这一行:
为什么它不可转换?
它们是不同的委托类型,并且不同委托类型之间没有引用转换(使用通用方差的通用委托类型除外)。您可以从如下所示的LoadingStartedEventHandler
创建Action
:
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 约定。按照惯例,事件是使用委托声明的,其中第一个参数是类型为 object
的"发送者",第二个参数是从 EventArgs
(或EventArgs
本身)派生的类型。这对你来说是否重要是由你决定的。如果您决定遵循约定,则基本上希望创建一个从持有ReturnState
EventArgs
派生的类型,然后将事件替换为:
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 术语,这些事件是"引发"而不是"启动"。
斯基特先生总是能做出很好的答案。在这里,我提供了您在问题中所说的另一种解决方案。
对于这两个:
- 为什么它不可转换?
- 我想在这些方法中操作和操作而不是特定的代表,可以吗?
斯基特先生已经回答了。
对于这两个:
- 我应该改用任何其他"类型",如操作吗?
- 我从这个"grooup"中知道的唯一两个是Func和Action,还有其他的吗?
您的fireUpEvent
方法可能不应接受 arbrary 类型的委托作为脆性设计。但这是可能的。
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
是拥有该方法的对象,如果它是静态方法,null
将被视为this
。 args
是传递给方法的参数列表。
这只是你可以实现它的一种方式,我很确定斯基特先生的答案绝对更好。