使用.NET 2.0或更高版本设置委托参数的最佳模式是什么?

使用.NET 2.0或更高版本设置委托参数的最佳模式是什么?,.net,vb.net,delegates,partial-application,.net,Vb.net,Delegates,Partial Application,有时,进行方法调用(包含参数)并将其转换为MethodInvoker非常有用,MethodInvoker将使用这些参数调用指定的函数,而不必在此时指定参数。在其他时候,做一些类似的事情很有用,但保留一些参数是开放的。这种类型的行为称为“咖喱”。在VB中执行此操作的最佳模式是什么 在VB 2010中可以使用lambda表达式,但是lambda表达式与“编辑并继续”不兼容,并且它们创建的闭包可能具有意外的引用行为。另一种方法是定义一些通用方法,如下所示: Public Module CurryMag

有时,进行方法调用(包含参数)并将其转换为MethodInvoker非常有用,MethodInvoker将使用这些参数调用指定的函数,而不必在此时指定参数。在其他时候,做一些类似的事情很有用,但保留一些参数是开放的。这种类型的行为称为“咖喱”。在VB中执行此操作的最佳模式是什么

在VB 2010中可以使用lambda表达式,但是lambda表达式与“编辑并继续”不兼容,并且它们创建的闭包可能具有意外的引用行为。另一种方法是定义一些通用方法,如下所示:

Public Module CurryMagic
    Delegate Sub Action(Of T1, T2)(ByVal P1 As T1, ByVal P2 As T2)
    Delegate Sub Action(Of T1, T2, T3)(ByVal P1 As T1, ByVal P2 As T2, ByVal P3 As T3)

    Class CurriedAction0(Of FixedType1, FixedType2)
        Dim _theAction As Action(Of FixedType1, FixedType2)
        Dim _FixedVal1 As FixedType1, _FixedVal2 As FixedType2
        Sub Exec()
            _theAction(_FixedVal1, _FixedVal2)
        End Sub
        Sub New(ByVal theAction As Action(Of FixedType1, FixedType2), _
                ByVal FixedVal1 As FixedType1, ByVal FixedVal2 As FixedType2)
            _theAction = theAction
            _FixedVal1 = FixedVal1
            _FixedVal2 = FixedVal2
        End Sub
    End Class

    Class CurriedAction1(Of ArgType1, FixedType1, FixedType2)
        Dim _theAction As Action(Of ArgType1, FixedType1, FixedType2)
        Dim _FixedVal1 As FixedType1, _FixedVal2 As FixedType2
        Sub Exec(ByVal ArgVal1 As ArgType1)
            _theAction(ArgVal1, _FixedVal1, _FixedVal2)
        End Sub
        Sub New(ByVal theAction As Action(Of ArgType1, FixedType1, FixedType2), _
                ByVal FixedVal1 As FixedType1, ByVal FixedVal2 As FixedType2)
            _theAction = theAction
            _FixedVal1 = FixedVal1
            _FixedVal2 = FixedVal2
        End Sub
    End Class

    Class ActionOf(Of ArgType1)
        Shared Function Create(Of FixedType1, FixedType2)(ByVal theSub As Action(Of ArgType1, FixedType1, FixedType2), ByVal FixedVal1 As FixedType1, ByVal FixedVal2 As FixedType2) As Action(Of ArgType1)
            Return AddressOf New CurriedAction1(Of ArgType1, FixedType1, FixedType2)(theSub, FixedVal1, FixedVal2).Exec
        End Function
    End Class

    Function NewInvoker(Of FixedType1, FixedType2)(ByVal theSub As Action(Of FixedType1, FixedType2), ByVal FixedVal1 As FixedType1, ByVal FixedVal2 As FixedType2) As MethodInvoker
        Return AddressOf New CurriedAction0(Of FixedType1, FixedType2)(theSub, FixedVal1, FixedVal2).Exec
    End Function
End Module
如果我想创建一个将执行Foo(5,“Hello”)的MethodInvoker,我可以使用

MyInvoker = NewInvoker(AddressOf Foo, 5, "Hello")
如果我想把我的动作(X)变成Boz(X,“George”,9),其中X是双精度的,我可以使用

MyAction = ActionOf(Of Double).Create(AddressOf Boz, "George", 9)
所有这些都非常流畅,只是需要有大量的样板代码来容纳不同数量的固定和非固定参数,并且委托创建语法中没有任何固有的东西来明确哪些参数是固定的,哪些是非固定的。有没有办法改善这种模式

增编:
如果委托是从结构成员函数创建的,那么该机制是什么?看起来委托获得了它自己的结构副本,但我不知道该副本是装箱的还是未装箱的。如果未装箱,则将CurryAction0和CurryAction1替换为structs将避免在创建委托时将CurryAction0或CurryAction1分配为单独的堆对象。但是,如果要将其装箱,则使用结构会增加将结构复制到装箱实例而不保存任何内容的开销

如果你能使用.NET4,那你呢


这并不能避免对每个
Func
和所有可能的“延迟”参数的样板要求,但我只想说明“简单”方法仍然相当干净。VB只是有点冗长,它似乎是一个有用的构造

此外,当前的
Curry
定义在调用中没有显式指定
类型时也无法隐式工作:-(

编辑:显示隐式选项对显式
Func
变量有效

选项显式打开
选项严格限制在
选项推断
导入系统
导入Microsoft.VisualBasic
模块电流测试
函数Test1(ByVal X作为字符串,ByVal Y作为字符串)作为字符串
返回X&Y
端函数
函数Test2(ByVal X为整数,ByVal Y为整数)为整数
返回X+Y
端函数
函数Test3(ByVal X作为整数,ByVal Y作为整数,ByVal Z作为字符串)作为字符串
返回Z&“:”&CStr(X+Y)
端函数
副标题()
Dim Curry1=Curry(字符串、字符串、字符串)(Test1的地址,“a”)
Dim Curry2=Curry(整数、整数、整数)(Test2、2的地址)
Dim Curry3=Curry(整数、整数、字符串、字符串的)(Test3、1、2的地址)
Dim f As Func(字符串,字符串,字符串)=Test1的地址
Dim g As Func(整型、整型、整型)=Test2的地址
Dim h As Func(整型、整型、字符串、字符串)=Test3的地址
暗咖喱4=咖喱(f,“b”)
暗咖喱5=咖喱(g,3)
暗咖喱6=咖喱(h、4、5)
控制台写入线(当前1(“b”))
控制台写入线(当前1(“c”))
控制台写入线(当前2(2))
控制台写入线(当前2(3))
控制台写入线(Curry3(“三”))
控制台写入线(Curry3(“3”))
控制台写入线(Curry4(“c”))
控制台写入线(Curry4(“d”))
控制台写入线(当前5(4))
控制台写入线(当前5(5))
控制台写入线(Curry6(“九”))
控制台写入线(当前6(“9”))
端接头
函数Curry(Of T,U,V)(ByVal Fn作为Func(Of T,U,V),ByVal Arg作为T)作为Func(Of U,V)
返回函数(Arg2为U)(Fn(Arg,Arg2))
端函数
函数Curry(属于T,U,V,W)(ByVal Fn作为Func(属于T,U,V,W),ByVal Arg1作为T,ByVal Arg2作为U)作为Func(属于V,W)
返回函数(Arg3为V)(Fn(Arg1、Arg2、Arg3))
端函数
端模块

查看ContiniousLinq的功能。它使用模板自动生成所有的curry函数

这就导致了

也许你可以把模板修改成VB


劳尔

如果你想问我C#4.0的问题,我会说:使用动态类型

但有趣的是,如果关闭Option Strict,VB一直支持动态键入

为了避免过多的样板代码,您可以尝试查看是否有可能使用可变数量的参数进行重载。这会更慢,但它是一个有用的安全网,可以确保您的代码适用于任何功能


我认为您可能需要闭包作为实现细节,但这没关系,不是吗?

我不确定您想要什么,但我有一个大胆的尝试:您可以创建委托而不指定参数,然后通过Invoke方法将其作为对象数组传递。这种类型的操作称为“Currying”“-实际上,这是部分应用。“Curry”指的是某些语言构造函数的方式,以使它们易于部分应用。@Tim Robinson:嗯……根据这种描述,Curry将是创建执行部分应用程序的方法的过程,与执行应用程序本身的行为不同?在我一直在寻找一种很好的方法。我的方法需要大量的样板代码才能工作,这很烦人,但避免了闭包带来的麻烦。顺便问一下,结构上的委托是装箱的还是未装箱的?如果未装箱,我的模式可以通过使用结构而不是类来改进,但是如果装箱,那将是一种浪费。@Bobby:这是一种浪费在调用之前可以忽略参数类型,但我更喜欢在实际情况下允许编译时类型检查
    ''Create new tuple instance with two items.
    Dim tuple As Tuple(Of Integer, String) = _
        New Tuple(Of Integer, String)(5, "Hello")
    ''Now you only have one argument to curry, packaging both parameters
    ''Access the parameters like this (strongly typed)
    Debug.Print tuple.Item1 '' 5
    Debug.Print tuple.Item2 '' "Hello"