如何从另一个类调用这种类型的扩展方法
Posted
技术标签:
【中文标题】如何从另一个类调用这种类型的扩展方法【英文标题】:How can I call this type of extension method from another class 【发布时间】:2020-09-03 04:28:33 【问题描述】:async Task<TResult> CancelAfterAsync<TResult>(Func<CancellationToken, Task<TResult>> startTask, TimeSpan timeout, CancellationToken cancellationToken)
using (var timeoutCancellation = new CancellationTokenSource())
using (var combinedCancellation = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCancellation.Token))
var originalTask = startTask(combinedCancellation.Token);
var delayTask = Task.Delay(timeout, timeoutCancellation.Token);
var completedTask = await Task.WhenAny(originalTask, delayTask);
// Cancel timeout to stop either task:
// - Either the original task completed, so we need to cancel the delay task.
// - Or the timeout expired, so we need to cancel the original task.
// Canceling will not affect a task, that is already completed.
timeoutCancellation.Cancel();
if (completedTask == originalTask)
// original task completed
return await originalTask;
else
// timeout
throw new TimeoutException();
Asynchronously wait for Task<T> to complete with timeout
我在 *** 找到了这个异步方法,并创建了这个方法的扩展方法:
public static async Task<TResult> CancelAfterAsync<TResult>(this Func<CancellationToken, Task<TResult>> startTask, TimeSpan timeout, CancellationToken cancellationToken)
using (var timeoutCancellation = new CancellationTokenSource())
using (var combinedCancellation = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCancellation.Token))
var originalTask = startTask(combinedCancellation.Token);
var delayTask = Task.Delay(timeout, timeoutCancellation.Token);
var completedTask = await Task.WhenAny(originalTask, delayTask);
// Cancel timeout to stop either task:
// - Either the original task completed, so we need to cancel the delay task.
// - Or the timeout expired, so we need to cancel the original task.
// Canceling will not affect a task, that is already completed.
timeoutCancellation.Cancel();
if (completedTask == originalTask)
// original task completed
return await originalTask;
else
// timeout
throw new TimeoutException();
但是我不知道如何在另一个类中调用这种类型的扩展方法。
async方法中第一个参数CancellationToken和最后一个参数cancellationToken是什么?
我想创建以下异步方法 UpdatePlayerCountryData 的任务,并将其与扩展方法一起使用来确定 UpdatePlayerCountryData 是否在 5 秒内完成,如果没有则抛出新的 TimeoutException();。我收到一条错误消息,因为我不知道缺少的两个参数是什么:
错误 CS0839:缺少参数
var test = await Extensionmethods.CancelAfterAsync( , UpdatePlayerCountryData("Germany", "Berlin"), new TimeSpan(0, 0, 0, 5, 0), );
如何将 UpdatePlayerCountryData 与扩展方法一起使用?如何从另一个类调用 CancelAfterAsync?
private async Task UpdatePlayerCountryData(string country, string city)
var resultprofile = await PlayFabClientAPI.UpdateUserDataAsync(new PlayFab.ClientModels.UpdateUserDataRequest()
Data = new Dictionary<string, string>()
"Country", country,
"City", city
,
Permission = PlayFab.ClientModels.UserDataPermission.Public
);
if (resultprofile.Error != null)
Console.WriteLine(resultprofile.Error.GenerateErrorReport());
else
Console.WriteLine("Successfully updated user data");
更新: 我改变了我的代码。方法 UpdatePlayerCountryData 会在 10 秒后使用 token.IsCancellationRequested 取消(TimeSpan.FromSeconds(10)),还是会在 await PlayFabClientAPI.UpdateUserDataAsync 花费超过 10 秒时取消?
是否将仅在等待 PlayFabClientAPI.UpdateUserDataAsync 完成后执行 (token.IsCancellationRequested),即使它需要几分钟?
if (token.IsCancellationRequested)
return;
完整代码:
public async Task PlayerAccountDetails()
TimeSpan timeout = TimeSpan.FromSeconds(10);
CancellationTokenSource cts = new CancellationTokenSource();
try
await UpdatePlayerCountryData("Country", "City", cts.Token).CancelAfter(timeout, cts.Token);
catch (Exception ex)
//catch exception here for logging
public static async Task CancelAfter(this Task @task, TimeSpan timeout, CancellationToken token)
var timeoutTask = Task.Delay(timeout, token);
var originalTask = @task;
var completedTask = await Task.WhenAny(timeoutTask, originalTask);
if (completedTask == timeoutTask)
throw new TimeoutException();
await completedTask;
private static async Task UpdatePlayerCountryData(string country, string city, CancellationToken token)
var resultprofile = await PlayFabClientAPI.UpdateUserDataAsync(new PlayFab.ClientModels.UpdateUserDataRequest()
Data = new Dictionary<string, string>()
"Country", country,
"City", city
,
Permission = PlayFab.ClientModels.UserDataPermission.Public
);
if (token.IsCancellationRequested)
return;
if (resultprofile.Error != null)
Console.WriteLine(resultprofile.Error.GenerateErrorReport());
else
Console.WriteLine("Successfully updated user data");
【问题讨论】:
你得到这个错误当你定义一个类的成员而不定义类时,命名空间不能直接包含字段或方法等成员。对于扩展方法,类应该是静态的,扩展方法的第一个方法应该在“this”关键字之前 请参考这个link,它将给出如何定义扩展方法。阅读文章后,请参考你提到的***链接写异步方法。 我将行更改为 public static async Task请检查代码以了解使用情况。在您的帖子中发布的 cancelafter 功能也是一种方法。另一种做同样事情的方法如下。 也为了清楚地了解以这种方式取消任务及其后果here
public static class Test
private static async Task UpdatePlayerCountryData(string country, string city)
var resultprofile = await PlayFabClientAPI.UpdateUserDataAsync(new PlayFab.ClientModels.UpdateUserDataRequest()
Data = new Dictionary<string, string>()
"Country", country,
"City", city
,
Permission = PlayFab.ClientModels.UserDataPermission.Public
);
if (resultprofile.Error != null)
Console.WriteLine(resultprofile.Error.GenerateErrorReport());
else
Console.WriteLine("Successfully updated user data");
public static async Task CancelAfter(this Task @task, TimeSpan timeout,CancellationToken token)
var timeoutTask = Task.Delay(timeout, token);
var originalTask = @task;
var completedTask = await Task.WhenAny(timeoutTask, originalTask);
if (completedTask == timeoutTask)
throw new TimeoutException();
await completedTask;
//Usage method
public static async Task ExtensionMethodUsage()
//Timeout ,for demonstartion purpose i used 10 seconds
//Modify according to your need
TimeSpan timeout = TimeSpan.FromSeconds(10);
//Cancellation Token
CancellationTokenSource cts = new CancellationTokenSource();
//U can manually cancel the task here if a caller of the function
//does not want to wait for timeout to occur.
//Some mechanism to cancel the task.
// If dont have a provision to cancel the task(which is not a good pattern)
//There is no need to use cancellation token
cts.Cancel();
try
//Pass the cancellation token to method
//When Cancel() is called all task will be cancalled.
await UpdatePlayerCountryData("Country", "City",cts.Token).CancelAfter(timeout, cts.Token);
catch (Exception)
//catch exception here for logging
取消令牌的使用不正确。正确的用法应该是这样的
private static async Task UpdatePlayerCountryData(string country, string city, CancellationToken token)
// UpdateUserDataAsync should take the Cancellation token,
// but I am afraid this method has any provision.
var resultprofile = await PlayFabClientAPI.UpdateUserDataAsync(new PlayFab.ClientModels.UpdateUserDataRequest()
Data = new Dictionary<string, string>()
"Country", country,
"City", city
,
Permission = PlayFab.ClientModels.UserDataPermission.Public
);
// Yes it would execute after the above await is complete
if (token.IsCancellationRequested)
return;
if (resultprofile.Error != null)
Console.WriteLine(resultprofile.Error.GenerateErrorReport());
else
Console.WriteLine("Successfully updated user data");
【讨论】:
我不明白取消在您的代码中是如何工作的。为什么在 var completedTask = await Task.WhenAny(timeoutTask, originalTask); 之后不使用 .Cancel()? 我已经用 cmets 和取消调用的用法更新了帖子。在继续之前,我想知道 UpdateUserDataAsync 是否有一个需要取消令牌的重载?如果不是,则扩展方法中根本不需要取消令牌,因为无论如何您的任务都无法取消。 如果代码抛出TimeoutException
,你为什么要抓Exception
?
它可以用于日志记录。万一异常需要传播给调用者,然后在记录后重新抛出。如果不需要记录,请删除 try catch 子句。
不,我不认为 UpdateUserDataAsync 有一个需要取消令牌的重载。是否可以在我的情况下使用 TaskStatus 来确定任务是否成功完成(RanToCompletion)?如果 TaskStatus 为 Canceled 或 Faulted,我应该再次运行该任务吗? docs.microsoft.com/en-us/dotnet/api/…以上是关于如何从另一个类调用这种类型的扩展方法的主要内容,如果未能解决你的问题,请参考以下文章