2014-10-16 33 views
0

有没有办法将参数绑定到委托来创建一个新的零参数委托?.NET C++:如何将参数绑定到委托以创建新的零参数委托?

喜欢的东西:

delegate void FnDelegate(int id, String^ name); 
void Fn(int id, String^ name); 

Delegate^ del = <some-function-name>(gcnew FnDelegate(&Fn), 1, "Name"); 

我注意到,表::调用可能使用了一些类似的机制,所以必须有一些这方面的设施:

delegate void FnDelegate(int id, String^ name); 
Invoke(gcnew FnDelegate(&Fn), 1, "Name"); 

现在,我要做的(对于每个功能!):

void Fn(int id, String^ name); 

ref class FnWrapper 
{ 
public: 
    int  id; 
    String^ name; 

    void Execute() 
    { 
     Fn(id, name); 
    } 
}; 

delegate void VoidDelegate(); 
Delegate^ CreateFnDelegate(int id, String^ name) 
{ 
    FnWrapper^ Wrapper = gcnew FnWrapper; 
    Wrapper->id = id; 
    Wrapper->name = name; 
    return gcnew VoidDelegate(Wrapper, &FnWrapper::Execute); 
} 

必须有一个mo重新做这个优雅的方式。

回答

1

C++有几个机制,boost::bind变成std::bind,现在我们有lambda表达式。尽管如此,它们不适用于托管类型。希望未来版本的C++/CLI编译器会添加lambda函数,直到那时你的方法与helper对象相差无几(但是你可以通用化它来减少代码的重复。两者都存储函数作为委托调用,并使用参数类型的模板可以使这个更加可重用)。

请注意,更好的方法非常简单地自动构建辅助对象,实际上没有任何已知的方式来做出与众不同的方式。

Form::Invoke只是简单地将所有参数都放在array<Object^>中,并在调用时使用反射......您也可以这样做,但这是一个主要的性能问题。

ref class AnyDelegateWrapper 
{ 
public: 
    System::Delegate^ target; 
    array<System::Object^>^ args; 

    void Execute() 
    { 
     target->DynamicInvoke(args); 
    } 
}; 

System::Action^ mg_bind(System::Delegate^ d, ... array<System::Object^>^ args) 
{ 
    AnyDelegateWrapper^ w = gcnew AnyDelegateWrapper(); 
    w->target = d; 
    w->args = args; 
    return gcnew System::Action(w, &AnyDelegateWrapper::Execute); 
} 

Action^ del = mg_bind(gcnew FnDelegate(&Fn), 1, "Name"); 
+0

“Form :: Invoke只是简单地将所有参数放在一个数组中,并在调用时使用反射......你也可以这样做,但这是一个主要的性能问题。 我该怎么做?我不关心表现。 – Brian 2014-10-16 20:46:23

+0

@ user3215177:我添加了一段代码,但未编译或测试。让我知道它是如何工作的。 – 2014-10-16 20:55:30

+0

谢谢!正是我在找什么。完美的作品。 KISS的原则 - 好,干净,通用。没有复制和粘贴,但根据您的代码编写了我需要的代码。 – Brian 2014-10-16 23:25:03

1

你在这里想要做的就是变量捕获。

在C#中,您将通过定义一个内嵌的委托,这将做变量捕获了你这样做:

Action<int, string> delegateWithParams = ... 
Action delegateWithoutParams1 = delegate { delegateWithParams(7, "foo"); }; 
// or if you like lambda syntax: 
Action delegateWithoutParams2 =() => delegateWithParams(7, "foo"); 

C++/CLI没有内嵌委托或lambda表达式,所以你必须做手动变量捕获。这是我写的一个类,用于帮助变量捕获和我的测试程序。

如果您查看反编译的C#代码,您会发现C#编译器在执行变量捕获时基本上会做同样的事情:它创建一个帮助类来存储捕获的变量,而不带参数的委托在该类上定义以使用参数调用委托。

void SomeMethod(int i, String^ s) 
{ 
    Debug::WriteLine("SomeMethod was called with integer {0} and string '{1}'", i, s); 
} 

generic<typename T1, typename T2> 
public ref class VariableCapture 
{ 
private: 
    Action<T1,T2>^ delegateWithParams; 
    T1 item1; 
    T2 item2; 

    VariableCapture(Action<T1,T2>^ delegateWithParams, T1 item1, T2 item2) 
    { 
     this->delegateWithParams = delegateWithParams; 
     this->item1 = item1; 
     this->item2 = item2; 
    } 

    void RunDelegate() 
    { 
     this->delegateWithParams(item1, item2); 
    } 

public: 
    static Action^ Capture(
     Action<T1,T2>^ delegateWithParams, T1 item1, T2 item2) 
    { 
     VariableCapture<T1,T2>^ capture = 
      gcnew VariableCapture<T1,T2>(delegateWithParams, item1, item2); 
     return gcnew Action(capture, &VariableCapture<T1,T2>::RunDelegate); 
    } 
}; 

int main(array<System::String ^> ^args) 
{ 
    Action<int, String^>^ delegateWithParams = 
     gcnew Action<int, String^>(&SomeMethod); 

    Action^ delegateWithoutParams = 
     VariableCapture<int, String^>::Capture(delegateWithParams, 7, "foo"); 

    delegateWithoutParams(); 
} 

输出:

的someMethod被称为与

  • 一旦创建delegateWithoutParams整数7和字符串 '富',你并不需要保存引用捕获对象或具有参数的委托:它们将被delegateWithoutParams中的引用保留为活动状态。
  • 您需要为每个参数编写一个VariableCapture版本,以及Action<>Func<>的单独版本。因为这是(基本上)与C#所做的事情相同,所以这将与在C#中所做的一样。 (没有反射开销)