13

我在 Visual Studio 2012 中有这个 C# 代码。

public Task SwitchLaserAsync(bool on)
{
   return Task.Run(new Action(() => SwitchLaser(on)));
}

这将执行SwitchLaser方法(类的公共非静态成员MyClass)作为带有参数 bool 的任务。

我想在托管 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# 这样的解决方案,而不是创建辅助函数和成员(这不是线程安全的)。

4

5 回答 5

17

目前还没有任何方法可以做到这一点。

在 C# 中,您有一个闭包。在编写 C++/CLI 编译器时,仍在讨论 C++ 中闭包的标准化语法。值得庆幸的是,微软选择等待并使用标准 lambda 语法,而不是引入另一种独特的语法。不幸的是,这意味着该功能尚不可用。如果是,它看起来像:

gcnew Action([this, on](){ SwitchLaser(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,但不适用于托管 lambda)。

于 2012-10-25T16:27:28.097 回答
4

这是我今天下午写的通用代码,它可能会有所帮助(尽管它与这个问题不完全匹配)。也许这会帮助下一个偶然发现这个问题的人。

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));
于 2014-02-17T01:13:35.623 回答
2

这是有效的答案..已经测试过..将参数(int)传递给动作sampleFunction。

#include "stdafx.h"
#include "CLRSamples.h"

using namespace System;
using namespace System::Threading;
using namespace System::Threading::Tasks;
using namespace System::Collections;
using namespace System::Collections::Generic;

void CLRSamples::sampleFunction(Object^ number)
{
    Console::WriteLine(number->ToString());
    Thread::Sleep((int)number * 100);
}

void CLRSamples::testTasks()
{
    List<Task^>^ tasks = gcnew List<Task^>();

    for (int i = 0; i < 10; i++)
    {
        tasks->Add(Task::Factory->StartNew((Action<Object^>^)(gcnew Action<Object^>(this, &CLRSamples::sampleFunction)), i));
    }

    Task::WaitAll(tasks->ToArray());

    Console::WriteLine("Completed...");
}

int main(array<System::String ^> ^args)
{
    CLRSamples^ samples = gcnew CLRSamples();
    samples->testTasks();

    Console::Read();
    return 0;
}
于 2019-03-13T05:15:06.200 回答
1

当我想为执行不返回值的方法(retuns 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()));

使用 helper 类的方法可能不是最优雅的解决方案,但它是我能找到的在不支持 lambda 表达式的 C++/CLI 中使用的最佳解决方案。

于 2017-07-06T07:06:48.397 回答
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);
        }
于 2021-07-10T09:18:48.287 回答