以下代码解释了这个想法
private async void button1_Click(object sender, EventArgs e)
{
string result;
CancellationToken cancellationToken = new CancellationTokenSource(3000).Token;
try
{
result = await GetDataAsync(cancellationToken)
.ContextIfSuccess(); // Should use SynchronizationContext only if Task status is RanToCompletion
}
catch(OperationCanceledException)
{
/* Context is not required */
return;
}
catch (Exception ex)
{
/* Context is not required otherwise it can slow down UI Thread a little bit */
Log(ex.ToString());
return;
}
/* UI Thread only */
button1.Text = result;
}
问题是"是否可以像ContextIfSuccess()一样制作方法?"
答案 0 :(得分:2)
为了拥有您想要的方法,您需要创建自定义awaiter。它主要是样板文件,关键在于,当被要求添加延续时,使用当前同步上下文添加一个以在成功完成时运行,并且在没有运行完成时使用默认调度程序。
public struct CaptureContextOnSuccessAwaiter : INotifyCompletion
{
private Task task;
public CaptureContextOnSuccessAwaiter(Task task)
{
this.task = task;
}
public CaptureContextOnSuccessAwaiter GetAwaiter() { return this; }
public void OnCompleted(Action continuation)
{
if (SynchronizationContext.Current != null)
{
task.ContinueWith(t => continuation(),
CancellationToken.None,
TaskContinuationOptions.OnlyOnRanToCompletion,
TaskScheduler.FromCurrentSynchronizationContext());
task.ContinueWith(t => continuation(),
CancellationToken.None,
TaskContinuationOptions.NotOnRanToCompletion,
TaskScheduler.Default);
}
else
{
task.ContinueWith(t => continuation(),
CancellationToken.None,
TaskContinuationOptions.None,
TaskScheduler.Default);
}
}
public void GetResult() { task.GetAwaiter().GetResult(); }
public bool IsCompleted { get { return task.GetAwaiter().IsCompleted; } }
}
public struct CaptureContextOnSuccessAwaiter<T> : INotifyCompletion
{
private Task<T> task;
public CaptureContextOnSuccessAwaiter(Task<T> task)
{
this.task = task;
}
public CaptureContextOnSuccessAwaiter<T> GetAwaiter() { return this; }
public void OnCompleted(Action continuation)
{
if (SynchronizationContext.Current != null)
{
task.ContinueWith(t => continuation(),
CancellationToken.None,
TaskContinuationOptions.OnlyOnRanToCompletion,
TaskScheduler.FromCurrentSynchronizationContext());
task.ContinueWith(t => continuation(),
CancellationToken.None,
TaskContinuationOptions.NotOnRanToCompletion,
TaskScheduler.Default);
}
else
{
task.ContinueWith(t => continuation(),
CancellationToken.None,
TaskContinuationOptions.None,
TaskScheduler.Default);
}
}
public T GetResult() { return task.GetAwaiter().GetResult(); }
public bool IsCompleted { get { return task.GetAwaiter().IsCompleted; } }
}
public static CaptureContextOnSuccessAwaiter ContextIfSuccess(this Task task)
{
return new CaptureContextOnSuccessAwaiter(task);
}
public static CaptureContextOnSuccessAwaiter<T> ContextIfSuccess<T>(this Task<T> task)
{
return new CaptureContextOnSuccessAwaiter<T>(task);
}
答案 1 :(得分:1)
await
没有上下文(ConfigureAwait(false)
)。然后,如果所需条件为真,则切换到上下文。
因此,您需要捕获SynchronizationContext.Current
和Post
。
这与TaskAwaiter
无论如何都非常相似。它在没有上下文的情况下恢复,然后在调用者希望的情况下切换回上下文。
您应该可以将其转换为ContextIfSuccess
方法。基本上,克隆TaskAwaiter
源代码并决定是否在完成通知中Post
。我假设这个功能已经存在。代码必须查看ConfigureAwait(...)
值并有条件地应用上下文。