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

Sadly, you cannot constraint a type to inherit from System.Delegate. This limitation has frustrated me many times. The only way around this for you is to constrain the delegate to be a reference type and then do a nasty-ish cast:

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

You are not able to do a new T(f) because the T:new() constraint only allows a parameterless constructor. The good news is that this is unnecessary as T is already a delegate type. You would need to invoke it like:

    foo<d1>(out ptr, f1);
于 2012-10-05T15:19:55.017 回答
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);。使用这种方法,可以很容易地为任何所需的参数模式提供通用函数。

于 2012-10-05T15:26:29.720 回答