如何将await.WhenAny()与GetAwaiter扩展方法结合实现按钮点击等待
解决方案:将按钮点击事件包装为Task以兼容
Task.WhenAny 要让按钮点击能和Task.WhenAny配合使用,核心是把按钮点击事件转换为Task类型——因为Task.WhenAny仅接受Task/Task<T>作为参数,而非自定义的可等待对象。
1. 添加返回Task的扩展方法
修改你的ButtonAwaiterExtensions静态类,新增一个将按钮点击包装为Task的扩展方法:
public static class ButtonAwaiterExtensions { // 保留原有的GetAwaiter方法,支持直接await Button public static ButtonAwaiter GetAwaiter(this Button button) { return new ButtonAwaiter() { Button = button }; } // 新增:将按钮点击转换为Task,兼容Task.WhenAny/WhenAll public static Task WhenClickedAsync(this Button button) { var tcs = new TaskCompletionSource<object?>(); RoutedEventHandler? handler = null; handler = (sender, e) => { button.Click -= handler; tcs.TrySetResult(null); }; button.Click += handler; return tcs.Task; } // 保留原有的ButtonAwaiter类 public class ButtonAwaiter : INotifyCompletion { public bool IsCompleted => false; public void GetResult() { } public Button? Button { get; set; } public void OnCompleted(Action continuation) { RoutedEventHandler? h = null; h = (o, e) => { Button!.Click -= h; continuation(); }; Button!.Click += h; } } }
2. 使用Task.WhenAny等待任意按钮点击
现在你可以通过以下方式等待多个按钮中任意一个被点击:
// 等待Button1或Button2中任意一个被点击 await Task.WhenAny(Button1.WhenClickedAsync(), Button2.WhenClickedAsync());
额外优化:返回点击的按钮(可选)
如果需要知道是哪个按钮被点击,可以让方法返回Task<Button>:
public static Task<Button> WhenClickedAsync(this Button button) { var tcs = new TaskCompletionSource<Button>(); RoutedEventHandler? handler = null; handler = (sender, e) => { button.Click -= handler; tcs.TrySetResult(button); }; button.Click += handler; return tcs.Task; }
使用时可以获取被点击的按钮:
Button clickedButton = await Task.WhenAny(Button1.WhenClickedAsync(), Button2.WhenClickedAsync()); // 处理点击的按钮
为什么原来的方式不行?
Task.WhenAny的参数要求是IEnumerable<Task>,它只识别标准的Task类型,不会处理自定义的INotifyCompletion可等待对象。通过将按钮点击包装为Task,就能完美兼容Task类的所有静态方法。
内容的提问来源于stack exchange,提问作者Tom667
相关产品推荐
相关产品推荐

