如何将带有参数的函数绑定到C#中的包中

时间:2018-08-28 12:47:03

标签: c# c++ templates generics

我正在使用C#编写线程池。

此线程池需要支持执行不同类型的功能。

下面是我需要的:

  1. 将函数及其参数绑定到函子(或称可调用对象)中,并且其签名应为void(void)
  2. 获取一个System.Threading.Tasks< TResult >代表我们可调用包的返回值

在CPP中,我可以使用一些模板魔术来做到这一点:

template <typename funtype, typename ...argstype>
std::future<typename std::result_of<funtype(argstype...)>::type> async(funtype&& func, argstype&&... args) {
    //start function body↓

    typedef std::packaged_task<std::result_of<funtype(argstype...)>::type(argstype...)> task_type;
    auto task = std::make_shared<task_type>(std::forward<funtype>(func));

    // bind to a callable object(functor) with signature void(void)
    auto whatINeed= std::bind([task](argstype... args) mutable {
    (*task)(std::forward<argstype>(args)...);
    }, std::forward<argstype>(args)...);

    //and we return the std::future which represents the return value of our package
    //in C#, i need to return an Task<TResult>
    return task->get_future();
}

在C#中,我现在写道:

public Task<TResult> async<TResult>(Delegate func, params object[] args)
{
    var stdPromiseXD = new TaskCompletionSource<TResult>();
    // the lambda is a callable object with signature void(void)
    works.Enqueue(() =>
    {
        try
        {
            stdPromiseXD.SetResult((TResult)func.DynamicInvoke(args));
        }
        catch (Exception ex)
        {
            stdPromiseXD.SetException(ex);
        }
    });
    // return the Task which equals std::future in CPP
    return stdPromiseXD.Task;
}

但是此C#版本不如CPP版本。首先,它不支持不返回功能,其次,在某些情况下,DynamicInvoke方法可能会明显变慢。

那么谁能告诉我如何在C#中更优雅地将函数和参数绑定到包中?

1 个答案:

答案 0 :(得分:2)

我建议使用Func<TResult>Action代替委托,然后在调用代码中使用闭包以简化用法。 Func是返回结果的通用强类型委托,而Action是不返回结果的通用强类型委托。

public Task<TResult> Enqueue<TResult>(Func<TResult> func)
{
    var stdPromiseXD = new TaskCompletionSource<TResult>();
    // the lambda is a callable object with signature void(void)
    works.Enqueue(() =>
    {
        try
        {
            stdPromiseXD.SetResult((TResult)func());
        }
        catch (Exception ex)
        {
            stdPromiseXD.SetException(ex);
        }
    });
    // return the Task which equals std::future in CPP
    return stdPromiseXD.Task;
}

public Task Enqueue(Action action)
{
    return Enqueue<object>(() =>
    {
        action();
        return null;
    });
}

我这样称呼:

var arg1 = "x1";
var arg2 = "2nd";
var arg3 = "third";

var resultTask1 = tp.Enqueue(() => DoConsoleWrite(arg1, arg2, arg3));
var resultTask2 = tp.Enqueue(() => SumAllNumbers(1, 2, 3, 4, 5));
var resultTask3 = tp.Enqueue(() => ThrowException());

while (tp.Pop()) { }

resultTask1.GetAwaiter().GetResult();
var result2 = resultTask2.GetAwaiter().GetResult();
var result3Exception = resultTask3.Exception;

使用闭包的替代方法是为func(Func<TResult>, Func<T1,TResult>, Func<T1,T2,Result>, etcAction, Action<T1>, Action<T1,T2>, etc)的每个参数计数创建重载