运行时生成的未知类型的EventHandler

时间:2017-04-04 08:16:12

标签: vb.net linq event-handling expression

好的,所以我已经坚持了一段时间,结合了各种各样的问题解决方案。

情况:

我使用activator.CreateInstance(x)实例化一个x类型的对象

类型x可以变化但总是具有相同的属性和方法。 问题是类型x有一个事件ReadCompleted作为ReadCompleted(这是一个委托),类型y具有相同的事件,具有相同的委托但它的类型为ReadCompleted1,z => ReadCompleted2等......

解决方案: 我在运行时使用以下代码分配一个委托:

Dim Client As Object = ServiceProvider.GetService(TypeDict(Entity))
    Dim TaskCompletionSource As New TaskCompletionSource(Of Entity)()

    Dim addMethod As MethodInfo = Client.GetType().GetMethod("add_ReadCompleted")
    Dim removeMethod As MethodInfo = Client.GetType().GetMethod("remove_ReadCompleted")

    Dim self As Object = Nothing
    Dim getSelf As Func(Of Object) = Function() self

    Dim eventArgType As Type = Client.GetType().GetEvent("ReadCompleted").EventHandlerType.GetMethod("Invoke").GetParameters()(1).ParameterType

    Dim e As ParameterExpression = Expression.Variable(eventArgType)

    Dim ExpBlock As BlockExpression = Expression.Block({Expression.Parameter(GetType(Object)), e},
        Expression.Call(
            Nothing,
            Me.GetType().GetMethod("ProcessTask"),
            Expression.Convert(e, eventArgType),
            Expression.Constant(TaskCompletionSource)),
    Expression.Call(
        Expression.Constant(Client),
        removeMethod,
        Expression.Convert(
            Expression.Invoke(
                Expression.Constant(getSelf)),
            addMethod.GetParameters()(0).ParameterType)
        )
        )

    Dim lambda As LambdaExpression = Expression.Lambda(
        addMethod.GetParameters()(0).ParameterType,
        ExpBlock,
        Expression.Parameter(GetType(Object)),
        Expression.Parameter(eventArgType))
    Dim dlg As [Delegate] = lambda.Compile()
    self = dlg

    addMethod.Invoke(Client, {dlg})

    Client.ReadAsync(PrimaryKey)

现在我对linq及其Expression类的知识有限,我尽力研究msdn文档,但我无法弄清楚:

方法ProcessTask被正确调用,我已经工作了足够长的时间,但我的参数e总是Nothing,导致NullReferenceException。 方法:

Public Shared Sub ProcessTask(ByVal e, ByRef tcs)
    'Console.WriteLine(e.GetType())
    If e.Error IsNot Nothing Then
        tcs.TrySetException(e.Error)
    ElseIf e.Cancelled Then
        tcs.TrySetCanceled()
    Else
        tcs.TrySetResult(e.Result)
    End If
End Sub

我不知道为什么,根据我的看法,这是调用我的方法的正确方法,但显然它不是。有人能指出我正确的方向吗? 可能是我只是盲目而且错过了一些非常明显的事情......

提前致谢!

编辑: 在等待答案的同时,我试图做更多的调试(在这种情况下是地狱)看到了如果我这样做:

Dim s As ParameterExpression = Expression.Variable(GetType(Object), "Sender")
    Dim ExpBlock As BlockExpression = Expression.Block({s, e},
                                               Expression.Call(
                                                   Nothing,
                                                   GetType(Console).GetMethod("WriteLine", New Type() {GetType(String)}),
                                                   Expression.Call(s, GetType(Object).GetMethod("ToString"))),
                                               Expression.Call(
            Expression.Constant(Client),
            removeMethod,
            Expression.Convert(
                Expression.Invoke(
                    Expression.Constant(getSelf)),
                addMethod.GetParameters()(0).ParameterType)
            ))

发件人作为对象参数也没什么,所以我感觉我的论点都没有通过......

希望这有助于更多地了解此事。

1 个答案:

答案 0 :(得分:0)

我想通了,原来我误解了Expression的整个参数:

Dim Client As Object = ServiceProvider.GetService(TypeDict(Entity))     Dim TaskCompletionSource As New TaskCompletionSource(Of Entity)()

Dim addMethod As MethodInfo = Client.GetType().GetMethod("add_ReadCompleted")
Dim removeMethod As MethodInfo = Client.GetType().GetMethod("remove_ReadCompleted")

Dim self As Object = Nothing
Dim getSelf As Func(Of Object) = Function() self

Dim eventArgType As Type = Client.GetType().GetEvent("ReadCompleted").EventHandlerType.GetMethod("Invoke").GetParameters()(1).ParameterType

Dim s As ParameterExpression = Expression.Parameter(GetType(Object), "Sender")
Dim e As ParameterExpression = Expression.Variable(eventArgType, "EventArgs")

Dim ExpBlock As BlockExpression = Expression.Block(\*{Expression.Parameter('GetType(Object)), e},*\ <--- this has to go
    Expression.Call(
        Nothing,
        Me.GetType().GetMethod("ProcessTask"),
        Expression.Convert(e, eventArgType),
        Expression.Constant(TaskCompletionSource)),
Expression.Call(
    Expression.Constant(Client),
    removeMethod,
    Expression.Convert(
        Expression.Invoke(
            Expression.Constant(getSelf)),
        addMethod.GetParameters()(0).ParameterType)
    )
    )

Dim lambda As LambdaExpression = Expression.Lambda(
    addMethod.GetParameters()(0).ParameterType,
    ExpBlock,
    s,
    e)
    \*Expression.Parameter(GetType(Object)),
    Expression.Parameter(eventArgType))*\ <-- this has to change
Dim dlg As [Delegate] = lambda.Compile()
self = dlg

addMethod.Invoke(Client, {dlg})

Client.ReadAsync(PrimaryKey)

因此,我认为参数不必在表达式本身中定义,但是它们应该被创建,并且应该保留引用以在编译表达式的lambda中使用它们。 / p>

希望将来某个人对这个答案有一些用处,并感谢所有人对此进行了研究。