0

TL;DR

I'm looking for a way to either:

  • Create an Action that takes a ParamArray of Object
  • Use Expression to create an Action that matches the given number/type of generic parameters

Details

I'm writing a function that turns asynchronous calls into blocking calls. My function takes a task to run, a time-out value, and a collection of States (kind of like events) that might occur as a result. We have States defined for multiple numbers of generic parameters IState(of T), IState(of T1, T2), etc...

Function MakeBlocking(task As Action,
                      resultingStates As IEnumberable(of IState),
                      Optional ByVal millisecondsTimeout As Integer = -1) 
                      As Boolean

  Dim are As New AutoResetEvent(False)
  Dim onFinish As New Action(Sub() are.Set())

  For Each state In resultingStates
    state.Subscribe(onFinish)
  Next

  task.Invoke()

  Dim result = are.WaitOne(millisecondsTimeout)

  For Each state In resultingStates
    state.Unsubscribe(onFinish)
  Next

  Return result
End Function

I'd like to be able to accept a collection of IState that have any number and type of parameters. Since IState(Of T) inherits from IState, and IState(Of T1, T2) inherits from IState(Of T), I think IEnumerable(Of IState) will work. The problem is the Action I'm subscribing them to doesn't have matching parameters.

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

As you can see, if I have resulting states of IState(Of String, Boolean), IState(Of Integer) and IState(Of String), I'd need a separate Action for each of those. I'd really like to be able to do something like this:

Dim onFinish As New Action(Sub(ParamArray stuff As Object())(Sub() are.Set())

But I can't seem to find the validate syntax to do that (if it is even possible). It seems like the only other option is using Expression.Lambda to dynamically create methods I can subscribe to, but I haven't had much experience with expression trees before. Is there a way I can create an Expression from my OnFinish action?

I should note that the library that deals with States is outside of my control. The underlying code checks that the the parameters match in number and assignability (i.e IsAssignableFrom), so even though I'm passing everything in as IState, it's finding out what the underlying type really is, and then telling me I can't subscribe my Action to that State.

4

1 回答 1

0

这就是我想出的...

与代表

可以Delegate在 VB.Net 中创建一个ParamArray使用解决方法的. 不知道这是否会奏效。也可以用于Expression动态创建委托:

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

但是,这些选项都不起作用,因为我需要一个Action. 看起来我可以从 a中创建ActionDelegate一个,但我需要知道的泛型类型,Action所以我可以,在我的情况下,直到运行时才可用......(我相信你不能在不知道静态类型的情况下进行转换)。

我的解决方案

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

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

对于我的特定场景,这最终成为最简单/最干净的解决方案。

于 2012-07-25T16:32:30.887 回答