C#事件行动vs Func<;任务>;抛出异常
有了下一段代码,为什么事件的以下签名行为会有所不同 这个抛出异常:C#事件行动vs Func<;任务>;抛出异常,c#,events,async-await,C#,Events,Async Await,有了下一段代码,为什么事件的以下签名行为会有所不同 这个抛出异常: public event Action SomeEvent; public event Func<Task> SomeEvent; 此不会引发异常: public event Action SomeEvent; public event Func<Task> SomeEvent; 公共事件函数SomeEvent; 这是控制台应用程序的代码。使用动作变量声明事件会停
public event Action SomeEvent;
public event Func<Task> SomeEvent;
此不会引发异常:
public event Action SomeEvent;
public event Func<Task> SomeEvent;
公共事件函数SomeEvent;
这是控制台应用程序的代码。使用动作变量声明事件会停止应用程序并显示堆栈跟踪
internal static class Program
{
private static void Main()
{
var watcher = new Watcher();
watcher.Context.RaiseSomeEvent();
Console.ReadKey();
}
}
internal class Watcher
{
public Context Context { get; } = new Context();
public Watcher()
{
Context.SomeEvent += async () =>
{
Console.WriteLine($"Sleeping, current thread {Thread.CurrentThread.ManagedThreadId}");
await Task.Run(() => Thread.Sleep(1000));
Console.WriteLine($"Slept, current thread {Thread.CurrentThread.ManagedThreadId}");
throw new Exception();
};
}
}
internal class Context
{
public event Action SomeEvent;
//public event Func<Task> SomeEvent;
public void RaiseSomeEvent()
{
SomeEvent?.Invoke();
}
}
内部静态类程序
{
私有静态void Main()
{
var watcher=新的watcher();
watcher.Context.RaiseSomeEvent();
Console.ReadKey();
}
}
内部类监视程序
{
公共上下文{get;}=新上下文();
公众观察者()
{
Context.SomeEvent+=async()=>
{
WriteLine($“休眠,当前线程{thread.CurrentThread.ManagedThreadId}”);
等待任务。运行(()=>Thread.Sleep(1000));
WriteLine($“睡眠,当前线程{thread.CurrentThread.ManagedThreadId}”);
抛出新异常();
};
}
}
内部类上下文
{
公共事件行动;
//公共活动职能;
公共无效RaiseSomeEvent()
{
SomeEvent?.Invoke();
}
}
我读了一些文章,例如,但没有找到任何特定于事件的内容。事件正在抛出,但由于它被抛出到一个单独的线程中,并且主线程中没有等待任何内容,因此未观察到异常
要解决此问题以便观察到异常,您必须更改代码以等待,如下所示:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Demo
{
internal static class Program
{
private static async Task Main()
{
var watcher = new Watcher();
await watcher.Context.RaiseSomeEvent();
Console.ReadKey();
}
}
internal class Watcher
{
public Context Context { get; } = new Context();
public Watcher()
{
Context.SomeEvent += async () =>
{
Console.WriteLine($"Sleeping, current thread {Thread.CurrentThread.ManagedThreadId}");
await Task.Run(() => Thread.Sleep(1000));
Console.WriteLine($"Slept, current thread {Thread.CurrentThread.ManagedThreadId}");
throw new Exception();
};
}
}
internal class Context
{
//public event Action SomeEvent;
public event Func<Task> SomeEvent;
public async Task RaiseSomeEvent()
{
await SomeEvent?.Invoke();
}
}
}
使用系统;
使用系统线程;
使用System.Threading.Tasks;
名称空间演示
{
内部静态类程序
{
专用静态异步任务Main()
{
var watcher=新的watcher();
wait watcher.Context.RaiseSomeEvent();
Console.ReadKey();
}
}
内部类监视程序
{
公共上下文{get;}=新上下文();
公众观察者()
{
Context.SomeEvent+=async()=>
{
WriteLine($“休眠,当前线程{thread.CurrentThread.ManagedThreadId}”);
等待任务。运行(()=>Thread.Sleep(1000));
WriteLine($“睡眠,当前线程{thread.CurrentThread.ManagedThreadId}”);
抛出新异常();
};
}
}
内部类上下文
{
//公共事件行动;
公共活动职能;
公共异步任务RaiseSomeEvent()
{
等待某个事件?.Invoke();
}
}
}
当您使用Action
时,您正在将lambda表达式转换为async void
,而Func
使其成为async Task
异步void方法具有不同的错误处理语义。当
异常从异步任务或异步任务方法中抛出
异常被捕获并放置在任务对象上。使用异步void
方法,则没有任务对象,因此从
异步void方法将直接在
SynchronizationContext,当异步void方法
开始
因此,无论使用何种委托类型,都会引发异常,但是,Func
版本会将异常放置在任务上,而Action
会直接引发异常
wait
ing使Task
打开异常并引发异常:
public event Func<Task> SomeEvent;
public async Task RaiseSomeEvent()
{
await SomeEvent?.Invoke();
}
公共事件函数SomeEvent;
公共异步任务RaiseSomeEvent()
{
等待某个事件?.Invoke();
}
这是事件lambda为async
的结果。相关的正确实现异步事件是很棘手的,因为您必须处理事件有多个订阅者的情况。您必须调用,然后将委托强制转换为特定类型的异步委托,然后调用它们以获取任务,最后决定如何等待它们(顺序或并发)。在这里看一个例子:我忽略了一个事实,即我可以等待内置委托调用方法wait SomeEvent?.Invoke()
。不知道为什么我认为它只有一个签名并且返回无效。所有的都是有效的分数。我只将前面的响应标记为answer,因为它引用了wait SomeEvent?.Invoke()
(我忽略了这一点,因此产生了混淆),并显式地引用了主线程上的返回。