我在Visual Studio 2012中有C#的代码。
public Task SwitchLaserAsync(bool on)
{
return Task.Run(new Action(() => SwitchLaser(on)));
}
这将执行SwitchLaser
方法(类MyClass
的公共非静态成员)作为参数bool on的任务。
我想在托管C ++ / CLI中做类似的事情。但我无法找到如何运行任务的任何方法,该任务将执行采用一个参数的成员方法。
目前的解决方案是这样的:
Task^ MyClass::SwitchLaserAsync( bool on )
{
laserOn = on; //member bool
return Task::Run(gcnew Action(this, &MyClass::SwitchLaserHelper));
}
SwitchLaserHelper
功能的实施:
void MyClass::SwitchLaserHelper()
{
SwitchLaser(laserOn);
}
必须有一些类似于C#的解决方案而不是创建辅助函数和成员(这不是线程安全的)。
答案 0 :(得分:12)
目前还没有办法做到这一点。
在C#中你有一个闭包。编写C ++ / CLI编译器时,仍在讨论C ++中闭包的标准化语法。值得庆幸的是,Microsoft选择等待并使用标准的lambda语法,而不是引入另一种独特的语法。不幸的是,这意味着该功能尚不可用。如果是,它将看起来像:
gcnew Action([on](){ SwitchLaserHelper(on) });
当前的线程安全解决方案是执行C#编译器所做的事情 - 将辅助函数和数据成员放入当前类中,但放入嵌套子类型中。当然,除了本地变量之外,你还需要保存this
指针。
ref class MyClass::SwitchLaserHelper
{
bool laserOn;
MyClass^ owner;
public:
SwitchLaserHelper(MyClass^ realThis, bool on) : owner(realThis), laserOn(on) {}
void DoIt() { owner->SwitchLaser(laserOn); }
};
Task^ MyClass::SwitchLaserAsync( bool on )
{
return Task::Run(gcnew Action(gcnew SwitchLaserHelper(this, on), &MyClass::SwitchLaserHelper::DoIt));
}
C ++ lamdba语法将为您创建该助手类(目前它适用于本机lambda,但尚未用于托管类)。
答案 1 :(得分:3)
这是我今天下午写的通用代码,可能有所帮助(虽然它不是这个问题的完全匹配)。也许这会帮助下一个偶然发现这个问题的人。
generic<typename T, typename TResult>
ref class Bind1
{
initonly T arg;
Func<T, TResult>^ const f;
TResult _() { return f(arg); }
public:
initonly Func<TResult>^ binder;
Bind1(Func<T, TResult>^ f, T arg) : f(f), arg(arg) {
binder = gcnew Func<TResult>(this, &Bind1::_);
}
};
ref class Binder abstract sealed // static
{
public:
generic<typename T, typename TResult>
static Func<TResult>^ Create(Func<T, TResult>^ f, T arg) {
return (gcnew Bind1<T, TResult>(f, arg))->binder;
}
};
用法是
const auto f = gcnew Func<T, TResult>(this, &MyClass::MyMethod);
return Task::Run(Binder::Create(f, arg));
答案 2 :(得分:0)
当我想为执行不返回值的方法的任务提供参数时,我遇到了类似的问题(重新调整void
)。因为Func<T, TResult>
不是我可以使用的选项。有关详细信息,请查看页面Using void return types with new Func。
所以我最终得到了一个解决方案,我创建了一个帮助类
template <typename T>
ref class ActionArguments
{
public:
ActionArguments(Action<T>^ func, T args) : m_func(func), m_args(args) {};
void operator()() { m_func(m_args); };
private:
Action<T>^ m_func;
T m_args;
};
使用Action<T>
委托来封装具有单个参数且不返回值的方法。
然后我会以下列方式使用这个助手类
ref class DisplayActivationController
{
public:
DisplayActivationController();
void StatusChanged(EventArgs^ args) { };
}
Action<EventArgs^>^ action =
gcnew Action<EventArgs^>(this, &DisplayActivationController::StatusChanged);
ActionArguments<EventArgs^>^ action_args =
gcnew ActionArguments<EventArgs^>(action, args);
Threading::Tasks::Task::Factory->
StartNew(gcnew Action(action_args, &ActionArguments<EventArgs^>::operator()));
使用帮助程序类的方法可能不是最优雅的解决方案,但是我能找到在C ++ / CLI中使用的最好的解决方案,它不支持lambda表达式。
答案 3 :(得分:0)
如果你使用的是c++/CLR,那么制作一个C# dll并添加对它的引用
namespace TaskClrHelper
{
public static class TaskHelper
{
public static Task<TResult> StartNew<T1, TResult>(
Func<T1, TResult> func,
T1 arg)
=> Task.Factory.StartNew(() => func(arg));
public static Task<TResult> StartNew<T1, T2, TResult>(
Func<T1, T2, TResult> func,
T1 arg1, T2 arg2)
=> Task.Factory.StartNew(() => func(arg1, arg2));
}
}
bool Device::Stop(int timeout)
{
_ResetEvent_Running->Set();
return _ResetEvent_Disconnect->WaitOne(timeout);
}
Task<bool>^ Device::StopAsync(int timeout)
{
auto func = gcnew Func<int, bool>(this, &Device::Stop);
return TaskClrHelper::TaskHelper::StartNew<int,bool>(func,timeout);
}