2012-10-05 77 views
1

在寫的P/Invoke包裝爲一個本地的dll的通用功能,我發現自己有很多的代碼看起來像這樣:約束在C#中的委託類型

// Declare delegate types matching some callbacks in the dll 
delegate void d1(T1, T1); 
delegate void d2(T1,T2,T3); 

// Some functions 
void f1(T1 a, T2 b) 
{ 
.. 
} 

void f2(T1 a, T2 b, T3 c) 
{ 
} 

後來的後來,

// Marshal some instantiated delegates into IntPtrs to pass to native dll 
IntPtr a = Marshal.GetFunctionPointerForDelegate(new d1(f1)); 
IntPtr b = Marshal.GetFunctionPointerForDelegate(new d2(f2)); 

所以我最終得到了很多類似上面的代碼。我想一些重構使用通用的功能可能是好的,是這樣的:

static void foo<T>(ref IntPtr ptr, T f) where T: System.Delegate, new() 
{ 
    ptr = Marshal.GetFunctionPointerForDelegate(new T(f)); 
} 

這將讓我接着寫:

foo<d1>(a,f1); 
foo<d2>(b,f2); 

等。它不會編譯!我試圖在函數聲明中添加一些類型約束,但無法使其工作。在這種情況下,對我來說並不重要,因爲重新分解幾乎不是很重要,但我只是想知道我會如何做這樣的事情?

回答

1

不幸的是,你不能約束一個類型從System.Delegate繼承。這種限制讓我很多次都感到沮喪。解決這個問題的唯一辦法對你來說是約束委託是引用類型,然後做一個討厭的上下的投:

static void foo<T>(out IntPtr ptr, T f) where T : class 
    { 
     ptr = Marshal.GetFunctionPointerForDelegate((Delegate)(object)f); 
    } 

你是不是能夠做一個new T(f)因爲T:new()約束只允許一個參數的構造函數。好消息是,這是不必要的,因爲T已經是代表類型。你需要像這樣調用它:

foo<d1>(out ptr, f1); 
+0

很好的解釋,謝謝你的解決方法。 –

0

沒有辦法編寫一個函數,它將採用「任何」委託並返回相同類型的委託。可以做的是爲零參數的委託,一個按值參數的委託,兩個按值參數的委託,三個按值參數的委託等編寫一系列通用函數,以及可能的代理一個ref參數,兩個ref參數,一個ref第一個參數和一個按值的第二個參數等。如果將自己限制爲具有按值參數的委託,則代碼複製的數量可能很煩人,但不是完全可怕(五如果您想處理多達四個參數的功能,則重複倍數;如果您想要最多八個功能,則重複九倍)。如果要處理ref和按值參數的任意組合,擴展會變得更糟(31個用於最多四個參數的功能,或511個最多八個功能)。

我的傾向是定義一個程序,其中包含您希望作爲文本資源的代碼,其中包含一些專門的「宏替代」,並且在執行時會將所有期望的參數組合擴展爲文本資源,結果放入一個文本框並選中它,以便將其複製並粘貼到源文件中。資源看起來是這樣的:

public class ActionCombiner$0 
{ 
    $1 _act1,_act2; 
    void Invoke($2) 
    { 
     _act1($3); 
     _act2($3); 
    } 
    ActionCombiner($1 act1, $1 act2) 
    { 
     _act1 = act1; 
     _act2 = act2; 
    } 
    public $1 Create($1 act1, $1 act2) 
    { 
     var temp = new ActionCombiner$0(act1, act2); 
     return temp.Invoke; 
    } 
} 

該程序將採取字符串資源並用各種字符串替換$ 0- $ 3。對於$ 0,泛型類型說明符列表(例如<T1,T2>)或無參數情況下的空字符串。對於$ 1,使用參數的委託類型。對於$ 2,包含類型的參數列表(例如T1 p1, T2 p2)。對於$ 3,不包含類型的參數列表(例如p1, p2);.使用這種方法,可以很容易地爲任何期望的參數模式提供通用函數。