【问题标题】:How do you access objects in the current application when invoking code compiled at runtime?调用运行时编译的代码时,如何访问当前应用程序中的对象?
【发布时间】:2012-12-12 05:18:16
【问题描述】:

我当前的项目是一个应用程序,它允许您用 C# 编写代码,然后使用 CSharpCodeProvider 执行它。只要代码本身是一个完整的应用程序,此时它就可以正常工作,例如:

using System;

namespace RuntimeCode {
    public static class Program {
        public static void Main() {
            Console.WriteLine("Hello, world!");
        }
    }
}

然后应用程序使用CompileAssemblyFromSourceGenerateInMemory = true 编译给定代码,然后在编译的程序集中调用Main 方法。

我的问题是,如何让编译后的代码访问主应用程序中的对象?

【问题讨论】:

  • 这是用于生产应用程序,还是用于娱乐/学习?如果是后者,您应该查看 Roslyn:msdn.microsoft.com/en-us/vstudio/roslyn.aspx 请注意,它没有适当的发布许可证,这意味着您不能将您的应用程序分发给其他人。
  • 搜索“C# scripting” - 有很多教程如何使用 C# 编写脚本并相应地将数据传递给新脚本。
  • @Alexei:似乎 OP 不是询问如何从脚本传递数据,而是从脚本访问主机应用程序。
  • 如何以及在何处加载生成的程序集?请参阅下面我对我的回答的评论。

标签: c# .net csharpcodeprovider


【解决方案1】:

请注意,运行时生成的代码将作为单独的进程执行。因此,您需要执行跨进程通信:在您的主应用程序、主机应用程序和新创建的应用程序之间。

实现跨进程或跨域交换的一个好方法是在您的主应用程序或默认应用程序域中托管一个特殊服务。生成的程序集将成为客户端。

我建议使用 WCF 和 NetNamedPipeBinding。您只需要配置合约和端​​点。


还请注意,GenerateInMemory=true 控制的唯一事情是将生成的程序集是否从磁盘加载到内存。就是这样。


如果您将脚本加载到默认的 AppDomain 中,那就更简单了。只需创建一个静态成员:

namespace MyNamespace
{
    public class Core
    {
        public static Core Instance { get; set; }
    }
}

并通过完整的类型名称访问它:

MyNamespace.Core.Instance;

【讨论】:

  • 我不确定 Manning 是否为编译后的代码(甚至没有保存到磁盘)启动单独的进程:“然后在编译的程序集中调用 Main 方法”对我来说听起来像反射调用.. .
  • @Alexei: CompileAssemblyFromSource 将我带到CodeDomProvider.CompileAssemblyFromSource,它会生成一个程序集。这是我看到的几个选项:将其加载到当前的 AppDomain(愚蠢)、新的 AppDomain(棘手)并作为可执行文件启动(简单)。但是我仍然认为您是正确的,并且 OP 从 #1 或 #2 开始执行某些操作。但又一次 - WCF/管道为我寻找实现 OP 想要的最简单的方法。
  • 如果CompilerResults results = codeProvider.CompileAssemblyFromFile(parameters, "test.cs"); 成功而不是results.CompiledAssembly 包含对已在当前域中加载的程序集的引用(对于GenerateInMemory=true 情况)。
  • @Alexei:谢谢,你给了我宝贵的想法和方向。
【解决方案2】:

您的注入代码无法访问主应用程序中定义的类和对象。(要访问主应用程序中定义的类,您可以再次在注入代码中使用反射,但对象无法访问。)

但是您可以简单地将数据作为参数发送到注入的代码:

using System;

namespace RuntimeCode {
    public static class Program {
        public static void StartPoint(object obj) {
            Console.WriteLine("Hello, world!");
        }
    }
}

然后通过发送包含所需数据的obj 对象来调用StartPoint 方法。

您可以使用您需要的任何类型的对象。但是应该被注入的代码识别出来。

【讨论】:

    【解决方案3】:

    然后应用程序编译给定的代码 […],然后在编译的程序集中调用 Main 方法。

    我认为这几乎可以回答您的问题。您正在编译的代码没有理由必须具有无参数的Main() 方法。例如,如果您需要访问的方法在IHost 接口中,那么您可以要求您的脚本有一个Main 方法,其参数类型为IHost。然后你会调用它,传入IHost 的当前实例。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多