.net 为未知数量和类型的泛型参数创建操作

.net 为未知数量和类型的泛型参数创建操作,.net,vb.net,generics,.net-3.5,expression-trees,.net,Vb.net,Generics,.net 3.5,Expression Trees,TL;DR 我正在寻找一种方法: 创建一个操作,该操作接受对象的参数数组 使用Expression创建与给定数量/类型的通用参数匹配的操作 详细信息 我正在编写一个函数,将异步调用转换为阻塞调用。我的函数接受一个要运行的任务、一个超时值和一组可能因此发生的状态(类似于事件)。我们为多个通用参数IState(of T)、IState(of T1、T2)等定义了状态 我希望能够接受具有任意数量和类型参数的IState集合。由于IState(Of T)继承自IState,而IState(Of T1

TL;DR

我正在寻找一种方法:

  • 创建一个
    操作
    ,该操作接受
    对象的
    参数数组
  • 使用
    Expression
    创建与给定数量/类型的通用参数匹配的操作
详细信息

我正在编写一个函数,将异步调用转换为阻塞调用。我的函数接受一个要运行的任务、一个超时值和一组可能因此发生的
状态(类似于事件)。我们为多个通用参数IState(of T)、IState(of T1、T2)等定义了
状态

我希望能够接受具有任意数量和类型参数的IState集合。由于
IState(Of T)
继承自
IState
,而
IState(Of T1,T2)
继承自
IState(Of T)
,我认为
IEnumerable(Of IState)
将起作用。问题是我订阅的
操作
没有匹配的参数

Public Interface IState(Of T1, T2)
  Inherits IState(Of T1)

  Shadows Function Subscribe(ByVal action As Action(Of T1, T2)) As IStateSubscription
  Shadows Function Unsubscribe(ByVal action As Action(Of T1, T2)) As Boolean

End Interface
如您所见,如果我的结果状态为
IState(字符串的,布尔值)
IState(整数的)
IState(字符串的)
,那么我需要为每种状态分别设置
操作。我真的很想能够做这样的事情:

Dim onFinish As New Action(Sub(ParamArray stuff As Object())(Sub() are.Set())
Public Function GetThing() As Boolean 'Returns success vs timeout
  Return MakeBlocking(Sub() SendAsyncThingRequest(), 
                      {ResultingState1, ResultingState2}, 
                      timeOutVal)
End Sub
但我似乎找不到验证语法来实现这一点(如果可能的话)。看起来唯一的选择是使用Expression.Lambda动态创建我可以订阅的方法,但我以前对表达式树没有太多经验。是否有一种方法可以从我的
OnFinish
操作创建
表达式


我应该注意到,处理状态的库不在我的控制范围之内。底层代码检查参数的数量和可分配性是否匹配(即IsAssignableFrom),因此即使我将所有内容作为
IState
传递,它也会找出底层类型的真正含义,然后告诉我我不能订阅我的
操作
到该
状态

以下是我的想法

与代表一起

在VB.Net中,可以使用。但不确定这是否有效。也可以使用
表达式
动态创建委托:

Dim are As New AutoResetEvent(False)

Dim setMethod = are.GetType.GetMethod("Set")
Dim callSet = Expression.Call(Expression.Constant(are), setMethod)

For Each state In resultingStates
  Dim args = state.GetType.GetGenericArguments()
  Dim params = Enumerable.Range(1, args.Count).Select(
                 Function(x) Expression.Parameter(args(x - 1), "var" & x))
  Dim onFinish = Expression.Lambda(callSet, params.ToArray).Compile()

  state.Subscribe(onFinish) 'Doesn't compile since OnFinish isn't an Action
Next
但是,这些选项都不起作用,因为我需要一个
操作
。看起来我可以,但我需要知道
操作的一般类型,这样我才能完成它,在我的例子中,在运行时之前是不可用的。。。(我相信如果不静态地了解类型,就无法进行强制转换)

我的解决方案

我最终把这个问题分解了一点。以前,我会这样称呼它:

Dim onFinish As New Action(Sub(ParamArray stuff As Object())(Sub() are.Set())
Public Function GetThing() As Boolean 'Returns success vs timeout
  Return MakeBlocking(Sub() SendAsyncThingRequest(), 
                      {ResultingState1, ResultingState2}, 
                      timeOutVal)
End Sub
相反,我将其打断,以便打电话的人承担更多的责任:

Public Function GetThing() As Boolean
  Dim are As New AutoResetEvent(False)

  WaitFor(ResultingState1, are)
  WaitFor(ResultingState2, are)

  SendAsyncThingRequest()

  Return are.WaitOne(timeoutVal)
End Function
我有一些通用方法“等待”结果状态,并在发生时设置
AutoResetEvent

Protected Sub WaitFor(Of T)(ByVal waitUpon As IState(Of T), 
                            ByVal are As AutoResetEvent)

  Dim action As New Action(Of T)(
  Sub(x)
    are.Set()
    waitUpon.Unsubscribe(action)
  End Sub)

  waitUpon.Subscribe(action)
End Sub
对于我的特定场景,这是最简单/最干净的解决方案