寻找与 lambda 表达式一起使用时 Reflection.Emit 行为的说明
Looking for clarification on the behavior of Reflection.Emit when used with a lambda expression
具体来说,我正在尝试创建一个接受 lambda 表达式的方法,然后在 运行 时间将该表达式作为临时控制台应用程序的主要方法分离出来,以允许分离出小的部分代码在 运行 时作为一个单独的进程,因此我可以更好地隔离内存行为(我确实研究了应用程序域,但由于我的用例的某些限制,运行 研究了那里的其他问题)。有点像一个非常有限的叉子。
如果可以假设 lambda 表达式只包含局部变量,这就相对简单了,但我正在努力弄清楚我必须做多少事情(以及最好的方法是) 如果表达式还使用非局部变量(即,根据下面的评论,存在于封闭范围内的变量。我当时想不出更好的方法来表达 "a variable that is not within the local scope but is accessible" ).
据我所知,非局部变量意味着将在 MSIL 中生成某种字段加载指令。虽然我可以在辅助应用程序中复制所需的 objects/fields,但如果我尝试通过 MethodBody.GetILAsByteArray() 按原样获取 lambda 表达式的 MSIL,那么生成的代码(我相信) 将包含以元数据 table 条目为目标的字段加载说明,这些条目可能(很可能会)不同于在控制台应用程序中创建的那些 objects/fields 副本的元数据 table 条目通过 Reflection.Emit。
更复杂的是闭包的问题,我 think/if 我记得正确意味着 lambda 表达式主体中的任何非本地引用都会导致创建一个对象,该对象将保存值(副本? 参考资料?我不太记得了)。不过我可能不需要担心这一点,因为在我的特定用例中它作为第二个应用程序发出后实际上不会是 lambda 表达式?如果我只是获取方法体,我想我最终会避开通常的关闭处理?
最后我有两个问题:
一个。在我对整个过程将如何运作的一般理解中有什么遗漏吗
乙。我是否必须去 MSIL 中仅字段 table 引用,如果是这样,最实用的方法是什么?有什么方法可以让 Reflection.Emit 为我做这些调整吗?
当然,我很高兴听到是否有一些不那么令人沮丧的方法来完成我正在尝试做的事情,并且愿意接受任何建议。
在匿名 method/lambda 表达式之外访问局部变量会创建一个包含变量的闭包对象。假设您将 lambda 作为委托传递,Target 属性 将包含委托的外部状态(类似于 DisplayClass)。除非你修改方法的CIL,否则你不会通过这个class从远程进程获得实时通信,但你可以简单地将它序列化并传递给远程进程。当然,如果委托依赖于静态字段,则剩下分析方法来查找它们并序列化它们(使用 System.Linq.Expressions 会有所帮助)。
现在,如果远程进程引用主程序集,它将在那里找到 DisplayClass,但如果没有,您甚至必须序列化它的类型并在另一方面,使用 AppDomain.TypeResolve 的方法将其挂钩。然后你可以用创建的类型反序列化对象。
具体来说,我正在尝试创建一个接受 lambda 表达式的方法,然后在 运行 时间将该表达式作为临时控制台应用程序的主要方法分离出来,以允许分离出小的部分代码在 运行 时作为一个单独的进程,因此我可以更好地隔离内存行为(我确实研究了应用程序域,但由于我的用例的某些限制,运行 研究了那里的其他问题)。有点像一个非常有限的叉子。
如果可以假设 lambda 表达式只包含局部变量,这就相对简单了,但我正在努力弄清楚我必须做多少事情(以及最好的方法是) 如果表达式还使用非局部变量(即,根据下面的评论,存在于封闭范围内的变量。我当时想不出更好的方法来表达 "a variable that is not within the local scope but is accessible" ).
据我所知,非局部变量意味着将在 MSIL 中生成某种字段加载指令。虽然我可以在辅助应用程序中复制所需的 objects/fields,但如果我尝试通过 MethodBody.GetILAsByteArray() 按原样获取 lambda 表达式的 MSIL,那么生成的代码(我相信) 将包含以元数据 table 条目为目标的字段加载说明,这些条目可能(很可能会)不同于在控制台应用程序中创建的那些 objects/fields 副本的元数据 table 条目通过 Reflection.Emit。
更复杂的是闭包的问题,我 think/if 我记得正确意味着 lambda 表达式主体中的任何非本地引用都会导致创建一个对象,该对象将保存值(副本? 参考资料?我不太记得了)。不过我可能不需要担心这一点,因为在我的特定用例中它作为第二个应用程序发出后实际上不会是 lambda 表达式?如果我只是获取方法体,我想我最终会避开通常的关闭处理?
最后我有两个问题:
一个。在我对整个过程将如何运作的一般理解中有什么遗漏吗
乙。我是否必须去 MSIL 中仅字段 table 引用,如果是这样,最实用的方法是什么?有什么方法可以让 Reflection.Emit 为我做这些调整吗?
当然,我很高兴听到是否有一些不那么令人沮丧的方法来完成我正在尝试做的事情,并且愿意接受任何建议。
在匿名 method/lambda 表达式之外访问局部变量会创建一个包含变量的闭包对象。假设您将 lambda 作为委托传递,Target 属性 将包含委托的外部状态(类似于 DisplayClass)。除非你修改方法的CIL,否则你不会通过这个class从远程进程获得实时通信,但你可以简单地将它序列化并传递给远程进程。当然,如果委托依赖于静态字段,则剩下分析方法来查找它们并序列化它们(使用 System.Linq.Expressions 会有所帮助)。
现在,如果远程进程引用主程序集,它将在那里找到 DisplayClass,但如果没有,您甚至必须序列化它的类型并在另一方面,使用 AppDomain.TypeResolve 的方法将其挂钩。然后你可以用创建的类型反序列化对象。