2012-07-24 106 views
0
的未知数量和类型

TL的动作; DR创建泛型参数

我正在寻找一种方式来之一:

  • 创建Action,需要一个ParamArrayObject
  • 使用Expression创建一个匹配给定数量/类型的通用参数的动作

细节

我正在写一个函数,将异步调用变成阻塞调用。我的函数需要运行一个任务,一个超时值和一个可能因此而发生的States(有点像事件)的集合。对于一般参数即将状态置(T)的,即将状态置(T1,T2的),等多个号码我们States定义...

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 

我希望能够接受即将状态置集合有任何数量和类型的参数。由于IState(Of T)继承自IState,并且IState(Of T1, T2)继承自IState(Of T),我认为IEnumerable(Of IState)将起作用。问题是Action我订阅他们没有匹配的参数。

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(Of String, Boolean)IState(Of Integer)IState(Of String)状态,我需要为每个单独Action。我真的希望能够做这样的事情:

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

但我似乎无法找到验证语法来做到这一点(如果它甚至有可能)。似乎唯一的选择是使用Expression.Lambda来动态创建我可以订阅的方法,但是我之前没有太多的表达式树的经验。有没有办法从我的OnFinish行动中创建Expression

我应该注意到,与国家打交道的图书馆不在我的控制范围之内。底层代码检查参数在数量和可分配性方面是否匹配(即IsAssignableFrom),因此即使我将所有内容都传递为IState,它会发现底层类型真的是,然后告诉我我不能请将我的Action订阅到State

回答

0

这就是我想出了...

与代表

它可以创建在VB.Net一个Delegate使用work-around,需要一个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。它看起来像我可以make an Action from a Delegate,但我需要知道的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 

我有通用的方法,即“WaitFor的“所产生的状态,并设置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 

这结束了对我的特殊情况下的简单/干净的解决方案。