1

ネイティブdllのP/Invokeラッパーを作成しているときに、次のようなコードがたくさんあることに気付きました。

// 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);

等々。コンパイルされません!関数宣言にいくつかの型制約を追加しようとしましたが、機能させることができません。この場合、リファクタリングはほとんど重要ではないため、それほど重要ではありませんが、このようなことをどのように行うかを知りたいだけです。

4

2 に答える 2

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);
于 2012-10-05T15:19:55.017 に答える
0

「任意の」デリゲートを受け取り、同じタイプのデリゲートを返す関数を作成する方法はありません。できることは、ゼロパラメータのデリゲート、1つの値によるパラメータのデリゲート、2つの値によるパラメータのデリゲート、3つの値によるパラメータのデリゲートなどの汎用関数のファミリを、おそらく1つのrefパラメーター、2つのrefパラメーター、ref最初のパラメーターと値による2番目のパラメーターなど。値によるパラメーターを受け取るデリゲートに自分自身を制限すると、コードの複製の量は煩わしいものになる可能性がありますが、完全に恐ろしいものではありません(5倍の複製)。最大4つのパラメーターの関数を処理する場合、または最大8つのパラメーターの関数を処理する場合は9倍)。の任意の組み合わせを処理したい場合ref値によるパラメーターの場合、展開はさらに悪化します(最大4つのパラメーターの関数の場合は31、最大8つの関数の場合は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);。このようなアプローチを使用すると、パラメータの任意のパターンにジェネリック関数をかなり簡単に提供できます。

于 2012-10-05T15:26:29.720 に答える