C# 类型';T';这是不可期待的

C# 类型';T';这是不可期待的,c#,task-parallel-library,task,async-await,C#,Task Parallel Library,Task,Async Await,我正在创建一个任务调度器,所以我试图创建某种重复函数来接受任务并等待它,但我遇到了一个奇怪的异常,即类型“T”不可等待 public static Task<T> Interval<T>(TimeSpan pollInterval, Func<T> action, CancellationToken token) { return Task.Factory.StartNew( async () => {

我正在创建一个任务调度器,所以我试图创建某种重复函数来接受任务并等待它,但我遇到了一个奇怪的异常,即
类型“T”不可等待

public static Task<T> Interval<T>(TimeSpan pollInterval, Func<T> action, CancellationToken token)
{
    return Task.Factory.StartNew(
        async () =>
        {
            for (; ; )
            {
                if (token.WaitCancellationRequested(pollInterval))
                    break;

                await action();
            }
        }, token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
}
公共静态任务间隔(TimeSpan pollInterval、Func action、CancellationToken)
{
return Task.Factory.StartNew(
异步()=>
{
对于(;;)
{
if(token.WaitCancellationRequested(pollInterval))
打破
等待行动();
}
},标记,TaskCreationOptions.LongRunning,TaskScheduler.Default);
}
那么,有谁能告诉我,我如何才能等待一个通用任务,因为我希望函数接受任何任务、任务、bool或任何其他类型的任务?

你不能

您可以创建一个采用通用异步函数的函数–一个返回
任务
的函数
那将是一个
Func

您还可以创建一个采用通用同步函数的函数,这就是您现在拥有的

您不能生成一个可以接受其中任何一个的函数,但可以生成两个重载


另一方面,函数从未实际使用函数的返回值。

因此,你根本不应该让它通用;相反,您应该执行
Func
操作

您不需要为此启动长时间运行的任务-只需直接使您的方法异步即可:

public static async Task RunAtIntervalAsync(TimeSpan pollInterval, Action action, CancellationToken token)
{
    while(true)
    {
        await Task.Delay(pollInterval, token);
        action();
    }
}
这将导致
操作
在当前上下文上运行。如果不需要,您可以使用:

await Task.Delay(pollInterval, token).ConfigureAwait(false);
action();
这将导致
操作
不在调用方的同一同步上下文上运行,并可能使用线程池线程


根据评论进行编辑:

如果您不希望生成的任务被取消,而只是在触发令牌时返回,则可以使用:

public static async Task RunAtIntervalAsync(TimeSpan pollInterval, Action action, CancellationToken token)
{
    while(!token.IsCancellationRequested)
    {
        try
        {
            await Task.Delay(pollInterval, token);
            action();
        }
        catch(OperationCanceledException e)
        {
            // Swallow cancellation - dangerous if action() throws this, though....
            break;
        }
    }
}
public static async Task RunAtIntervalAsync<T>(TimeSpan pollInterval, Func<Task<T>> fetchOperation, Action<T> operationOnResult, CancellationToken token)
{
    while(!token.IsCancellationRequested)
    {
        try
        {
            await Task.Delay(pollInterval, token);
        }
        catch(OperationCanceledException e)
        {
            // Swallow cancellation
            break;
        }

        // Get a value
        T value = await fetchOperation();

        // Use result (ie: update UI)
        operationOnResult(value);
    }
}

编辑2:

如果要传入
async
lambdas,应使该方法采用
Func
,而不是
操作

public static async Task RunAtIntervalAsync(TimeSpan pollInterval, Func<Task> actionTask, CancellationToken token)
{
    while(!token.IsCancellationRequested)
    {
        try
        {
            await Task.Delay(pollInterval, token);
        }
        catch(OperationCanceledException e)
        {
            // Swallow cancellation
            break;
        }

        await actionTask();
    }
}
在这里检查一个例子。 同意SLaks
,您需要使Func的泛型参数T可等待以使用等待。 例如,如果T是一个
字符串
,则代码将“等待”一个只返回
字符串的函数。

wait
仅对任务有效。欲了解更多信息,请查看此说明;示例在VB.net中。

为什么
Interval
返回
任务
?它永远不会结束(直到出现异常或It错误才会结束),而且它从来没有设置为结果的
T
值。@Servy no我有一个cancel Token Yes,如果它被取消,则任务被设置为cancel状态。它仍然没有结果。它在某个点上完成,但是它从来没有一个可以获取的
结果。我的观点是,您应该返回一个
任务,而不是
任务
,并且您应该接受一个
操作
,而不是
函数
,因为您放弃了函数的结果。@Servy:很抱歉,我无法理解您的想法;但是如果你理解我的问题。我想要实现的是,我想要函数接受任何类型的任务,int或bool或任何类型的任务。。。这能实现吗?或者我该怎么做呢请注意,您的函数在延迟期间浪费了一个线程。您应该使用
wait Task.Delay()
相反:请您通过发布代码片段来更详细地回答问题,或者更清楚地说明问题,因为我听不懂您的意思?@RuneS:您不明白哪一部分?我想清楚的是,在这个函数中,我得到了一个等待的方法,可以重复,直到我通过i cancelation token取消它为止。。。因此,假设我有一个返回值而不是空值的任务。@RuneS:awaitable函数是指返回
task
,而不是
T
的函数。这意味着您要编写
Func
.static类CancellationTokenExtensions{public static bool WaitCancellationRequested(此CancellationToken,TimeSpan timeout){return token.WaitHandle.WaitOne(timeout);}}我可以将while(true)改为while(!token.WaitCancellationRequested)吗(pollInterval))@RuneS是阻塞的。上面是完全异步的(非阻塞)@RuneS:与Reed的代码不同,您的方法在延迟期间会浪费一个线程。Reed的代码是正确的编写方法。让我们来看看
RunAtIntervalAsync(TimeSpan.FromSeconds(1), 
   async () => { await Task.Delay(1000); return "Foo"; },
   result => UpdateUI(result),
   token);