在C#中使用Func with Task

时间:2015-07-01 07:06:58

标签: c# delegates

我在尝试理解Func在C#中的确切运作方式时遇到了一些问题,尽管我已经阅读了一些关于它的好博客文章。

我有以下情况: 我正在构建一个小型计费系统,它使用Azure Service Bus队列来处理账单。用户可以初始化两个进程:

  1. 之前创建一个日志,以检查交易是否正确
  2. 为客户创建实际发票
  3. 为此,我提出了一种初始化计费任务(包含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方法在m ProcessBillingQueue类上运行ServiceBus方法,这意味着它无法运行ProcessDaybookQueue

    我最初的想法是只使用Func<string, int, int, bool>并使StartQueueProcess方法返回一个bool(因为Func需要返回一些东西),使它看起来像这样:

    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)返回无效。让它返回Func<string, int, int, bool>没有错误。但为什么呢?我不应该能够让它返回我想要的东西(即无效)吗?

    有人可以对此有所了解吗? : - )

2 个答案:

答案 0 :(得分:2)

您不需要Func,需要ActionFunc是您的委托具有返回值的时间。当没有返回值时,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 :(得分:0)

我看到两个不同的问题: 我担心Task.Factory.StartNew(() => processMethod)不会做你期望它做的事情。它不会执行processMethod。 2. StartQueueProcess被定义为接受Func&lt;&gt;功能有三个参数。它没有提到参数本身。你有责任为这个lambda提供一些参数。

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

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

E.g。这个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));
}