c# - 如何将带参数的函数绑定到 C# 中的包中
问题描述
我正在编写一个使用 C# 的线程池。
这个线程池需要支持执行不同类型的函数。
以下是我需要的:
- 将一个函数及其参数绑定到一个仿函数(或者说可调用对象)中,它的签名应该是 void(void)
- 得到一个
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# 中的包中?
解决方案
我建议使用Func<TResult>
andAction
而不是委托,然后在调用代码中使用闭包来简化使用。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>, etc
and Action, Action<T1>, Action<T1,T2>, etc
)的每个参数计数创建重载
推荐阅读
- python - Python更快的方法将每一行数据帧作为一个表插入到mysql
- aws-lambda - Bitbucket Pipeline 将 .Net Core API 部署到 AWS 无服务器应用程序模型
- javascript - 最初如何创建具有 N 个 div 的 HTML 页面
- flask - compute.beta.regionAutoscaler 错误烧瓶应用程序在谷歌云上部署时出错
- ios - 当我尝试从 App Delegate 访问根控制器时,window.rootViewController 返回 nil
- python - 从python中的字典中删除元素
- mongodb - MongoDB数组作为字符串
- php - 我无法选择在 laravel 中有部门的用户
- python - 根据关键字转推特定用户的推文
- xml - DB2 中的 XML 解析 - 在单个 XML CLOB 字段中生成多行表达式