2014-10-16 72 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#中所做的一樣。 (沒有反射開銷)