限制在c#中委托类型的通用函数

时间:2012-10-05 15:06:34

标签: c# generics delegates

在为本机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);

等等。它不编译!我试图在函数声明中添加一些类型约束,但无法使其工作。在这种情况下,对我来说并不重要,因为重新分解并不是很重要,但我只是想知道我会怎么做这样的事情?

2 个答案:

答案 0 :(得分: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);

答案 1 :(得分: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);.使用这种方法,可以相当容易地为任何所需的参数模式提供通用函数。