在 C# 中将 Func 与任务一起使用

Using Func with Task in C#

我在尝试理解 Func 在 C# 中的工作原理时遇到了一些问题,尽管已经阅读了一些关于它的好博文。

我有以下场景: 我正在构建一个小型计费系统,它使用 Azure 服务总线队列来处理计费。有两个进程可以被用户初始化:

  1. 创建一个日记本以检查之前的交易是否正确
  2. 为客户创建实际发票

为此,我制定了一个通用方法来初始化计费任务(包含 N 个计费),该任务采用 bool 参数来判断它是需要创建的日记本还是实际计费。在这个方法中,我 运行 进行了以下检查:

if (isDayBookProcessing)
{
    // Daybook processing code here   
}
else
{
    // Run queue process async
    StartQueueProcess(queueName, billingTaskId, numOfItemsEnqueued);
}

然后我得到了一个通用的 "StartQueueProcess" 方法,它是 运行 在它自己的 Task 中,像这样:

private void StartQueueProcess(string queueName, int billingTaskId, int numOfItemsEnqueued)
{
    Task.Factory.StartNew(() => _factory.AzureFactory.ServiceBus.ProcessBillingQueue(queueName, billingTaskId, numOfItemsEnqueued));
}

如您所见,StartQueueProcess 方法 运行 是 ServiceBusclass 上的 ProcessBillingQueue 方法,这意味着它不能运行一个ProcessDaybookQueue

我最初的想法是利用 Func<string, int, int, bool> 并使 StartQueueProcess 方法 return 成为布尔值(因为 Func 需要 return 一些东西) 使其看起来像这样:

if (isDayBookProcessing)
{
    // Daybook processing code here 
    StartQueueProcess(_factory.AzureFactory.ServiceBus.ProcessDaybookQueue(queueName, billingTaskId, numOfItemsEnqueued));  
}
else
{
    // Run queue process async   
    StartQueueProcess(_factory.AzureFactory.ServiceBus.ProcessBillingQueue(queueName, billingTaskId, numOfItemsEnqueued));
}

private bool StartQueueProcess(Func<string, int, int, bool> processMethod)
{
    Task.Factory.StartNew(() => processMethod);

    return true;
}

但是,这给出了一个错误,告诉我 Argument type 'void' is not assignable to parameter type 'System.Func<string, int, int bool>'

我的_factory.AzureFactory.ServiceBus.ProcessDaybookQueue(queueName, billingTaskId, numOfItemsEnqueued)return无效。使它 return Func<string, int, int, bool> 没有错误。但究竟是为什么?难道我不能 return 我想要的(即 void)吗?

任何人都可以阐明这一点吗? :-)

您不需要 Func,您需要 ActionFunc 是指您的代表具有 return 值。当没有 return 值时,Action 是合适的:

private void StartQueueProcess(Action<string, int, int> processMethod)
{
    Task.Factory.StartNew(() => processMethod);
}

注意,换行 async over sync is an anti-pattern。您最好在呼叫站点使用 Task.Run

if (isDayBookProcessing)
{
    // Daybook processing code here 
    Task.Run(() => _factory.AzureFactory.ServiceBus.ProcessDaybookQueue(
                           queueName, 
                           billingTaskId, 
                           numOfItemsEnqueued));  
}
else
{
    // Run queue process async   
    Task.Run(() => _factory.AzureFactory.ServiceBus.ProcessBillingQueue(
                           queueName,
                           billingTaskId, 
                           numOfItemsEnqueued));
} 

尽管我看到您正在调用相同的方法,所以我认为不需要 if-else

我看到两个不同的问题: 1. 恐怕 Task.Factory.StartNew(() => processMethod) 不会做你期望它做的事。它不会执行processMethod。 2. StartQueueProcess 被定义为接受一个具有三个参数的Func<> 函数。它没有说明参数本身。您有责任为这个 lambda 提供一些参数。

这里为什么要假设需要使用Func<>?你不必。你的 StartQueueProcess 可以这样:

private bool StartQueueProcess(
     string param1, 
     int param2, 
     int param3, 
     Action<string, int, int> processMethod)
{
    Task.Factory.StartNew(() => processMethod(param1, param2, param3));
}

例如此 StartNew 重载将愉快地接受任何 Action 参数。

此外,在这种特殊情况下,我认为根本不需要声明新函数。一个简单的 .StartNew 调用可能就可以了(虽然很接近):

if (isDayBookProcessing)
{
    // Daybook processing code here 
    Task.Factory.StartNew(() =>  _factory
        .AzureFactory
        .ServiceBus
        .ProcessDaybookQueue(queueName, billingTaskId, numOfItemsEnqueued));  
}
else
{
    // Run queue process async   
    Task.Factory.StartNew(() => _factory
        .AzureFactory
        .ServiceBus
        .ProcessBillingQueue(queueName, billingTaskId, numOfItemsEnqueued));
}