C# 正在寻找有关Reflection.Emit(当与lambda表达式一起使用时)行为的说明

C# 正在寻找有关Reflection.Emit(当与lambda表达式一起使用时)行为的说明,c#,.net,lambda,cil,reflection.emit,C#,.net,Lambda,Cil,Reflection.emit,具体地说,我正在尝试创建一个方法,该方法接受lambda表达式,然后在运行时将该表达式作为临时控制台应用程序的主方法旋转,以便允许在运行时将小部分代码旋转为单独的进程,以便更好地隔离内存行为(我确实研究了应用程序域,但由于我的用例的某些限制,在那里遇到了其他问题) 如果可以假设lambda表达式只包含局部变量,那么这是相对简单的,但是我正在努力弄清楚如果表达式还使用非局部变量,我需要做多少工作(以及最好的方法是什么)(也就是说,根据下面的评论,一个存在于封闭范围内的变量。当时我想不出更好的方式来

具体地说,我正在尝试创建一个方法,该方法接受lambda表达式,然后在运行时将该表达式作为临时控制台应用程序的主方法旋转,以便允许在运行时将小部分代码旋转为单独的进程,以便更好地隔离内存行为(我确实研究了应用程序域,但由于我的用例的某些限制,在那里遇到了其他问题)

如果可以假设lambda表达式只包含局部变量,那么这是相对简单的,但是我正在努力弄清楚如果表达式还使用非局部变量,我需要做多少工作(以及最好的方法是什么)(也就是说,根据下面的评论,一个存在于封闭范围内的变量。当时我想不出更好的方式来表达“一个不在本地范围内但可访问的变量”)

据我所知,非局部变量意味着MSIL中将生成某种类型的字段加载指令。虽然我可以在辅助应用程序中复制所需的对象/字段,但如果我尝试通过MethodBody.GetILAsByteArray()将lambda表达式的MSIL保持原样,则生成的代码(我相信)将包含字段加载指令,这些指令将针对元数据表项,这些元数据表项可能(很可能)不同于通过Reflection.Emit在控制台应用程序中创建的那些对象/字段的副本的元数据表项

更复杂的是,闭包(我认为/如果我记得正确的话)意味着lambda表达式体中的任何非局部引用都会导致创建一个保存值的对象(副本?引用?我不太记得了).不过我可能不需要担心,因为在我的特定用例中,当它作为第二个应用程序发出时,它实际上不会是lambda表达式?如果我只是得到方法体,我想我最终会避开通常的闭包处理

最后,我有两个问题:

答:在我对整个过程如何运作的总体理解中,是否有什么遗漏

B.我是否必须在MSIL中进行字段表引用,如果是这样,最实用的方法是什么?是否有任何方法可以获得Reflection.Emit来为我进行这些调整


当然,我很高兴听到是否有一些不那么令人沮丧的方法来完成我试图做的事情,并且愿意接受任何建议。

在匿名方法/lambda表达式外部访问局部变量会创建一个包含变量的闭包对象。假设您将lambda作为委托传递,则目标属性将t委托的外部状态(类似于DisplayClass)。除非您修改该方法的CIL,否则无法通过此类从远程进程获得实时通信,但您可以简单地序列化该类并将其传递给远程进程。当然,如果委托依赖于静态字段,则只需分析该方法以查找并序列化它们(使用System.Linq.Expressions会很有帮助)


现在,如果远程进程引用主程序集,它将在那里找到DisplayClass,但是如果没有,您将不得不序列化它的类型,并在另一端构建它,使用AppDomain.TypeResolve将其与方法挂钩。然后您可以使用创建的类型反序列化对象。

您是否可以从反编译lambda的表达式版本,并在编译之前修改代码?如果表达式不是自包含的,那么最糟糕的情况是,您必须序列化所有内部状态(以某种方式)因此,它可以在一个全新的应用程序中重新水化。闭包在这里毫无意义,因为编译器生成的任何魔法都不能跨越进程边界。鉴于一般问题是您不想解决的,我建议您不要解决它。要求所有此类代码都是自包含的,如果不是,则必须声明它是什么“需要作为简单标量类型的参数,必须显式提供。@JeroenMostert I实际上强烈考虑让用户指定任何外部使用的对象/变量的类型和当前值,但这仍然留下lambda的主体如何处理对外部变量的引用的问题,从我能告诉你的是,作为表引用,可能会有不正确的表索引,我需要以某种方式进行调整,以使用外部数据的封装。我想我可以在lambda中使该数据封装成为显式必需的参数,但我不知道如何强制缺少外部引用。你的问题不是maki如果您得到的是表达式树(
expression
)还是实际的委托,这一点是完全清楚的。如果您允许将任意方法作为表达式树(
()=>某物()传入,则这两种情况都相当复杂,因为您无法访问方法的实际定义。在理论上可能的话,修补生成的IL以完全在组件之外工作,对我来说似乎是非常复杂的。请考虑使用罗斯林,让人们通过实际的源代码。如果您可以提供源代码的执行。可以,您可以利用在运行时获取源代码。这样,您仍然可以拥有“自然”开发的源代码,同时也可以在运行时访问它。实际上,只需重新编译整个内容(或仔细选择的部分).这对分叉又有何影响?这也使得允许非局部性和拒绝依赖内部状态(通过简单地不提供内部状态)等事情变得容易。