C# CompilerResults GenerateInMemory?

标签 c# .net-assembly

我一直在关注这个 StackOverflow question .这是我能找到的最接近的东西,但不完全是。

在我提出问题之前,让我解释一下我的最终目标是什么,我正在制作一个支持网络的编译器平台,因此,我想在内存中做所有事情(不创建文件),所以我希望能够编译代码,然后能够引用我刚刚编译的类中的对象进行任意测试。我知道这听起来有多不安全,因此欢迎提供有关安全性的意见。

我的问题是如何将 C# 源代码编译到内存中,并创建该类的实例?

目前,我正处于可以生成有效的 .dll 并在 VisualStudio 中手动导入和使用它的阶段。

我接下来的两个步骤是:

  • 自动加载程序集(这就是我要问的)
    • 这意味着我不再需要提供 dll 的路径,并手动获取封闭类成员的地址
  • 任意引用它的成员
    • 这意味着我可以创建一个类的接口(interface),而无需预先了解类的成员,有点像 foreach 循环如何处理键值对。

为了完全在内存中尝试这个,我试过了。 (来源然后解释)

private object sourceToObj(string source) {
  string source = "...";  /*my class*/
  CSharpCodeProvider pro = new CSharpCodeProvider();

  CompilerParameters params = new CompilerParameters();
    params.GenerateInMemory = true;
    params.GenerateExecutable = false; 
    params.ReferencedAssemblies.Add("System.dll");

  CompilerResults res = pro.CompileAssemblyFromSource( params, source );

  Assembly DOTasm = res.CompiledAssembly;

  AppDomain domain = AppDomain.CreateDomain( "thisdomain" );
    domain.load( DOTasm , /*???*/ );
    domain.CreateInstanceAndUnwrap( DOTasm .FullName, /*???*/ );


  return /*???*/;
}

最后,正如代码中的这一点,我希望返回一些我可以调用其属性的对象。 所以调用 object obj = new sourceToObj(source).class(); 或其他东西是可能的。

沿着这条可能确实是错误的道路走下去给我留下了 3 个未知数。

  • 什么是 System.Security.Policy.Evidence assemblySecurity 对象。
  • AppDomain.CreateInstanceAndUnwrap() 的正确参数是什么?
  • 然后如何将其作为对象返回?

当然,这种方法可能是错误的,它基于上面的链接,该链接很接近,但不是火鸡。


编辑:经过更多研究后,我想包含一个源文件示例。

namespace testNS {
  public partial class i18Rule {
     private string i18_Name;
     private string i18_Value;
     public void setName(string s) {
         i18_name = s;
     }
     /* Other obvious functions */
  };
};

我相信我取得了一点进展并进入了我的问题的第二个子句,如何创建它的实例。

我继续使用 AppDomain 来包含我的程序集。我还采用了写入磁盘并将其读取为字节数组的方式,正如我在这个问题中所做的那样我碰巧遇到了 Compile c# on the fly .

/* not the final method, see Philips answer for tryLoadCompiledType which validates this works */
private void sourceToUnitTest(string source, callBack CB) {
    var pro = new CSharpCodeProvider();

    var DOMref = AppDomain.CurrentDomain.GetAssemblies()
            .Where(obj => !obj.IsDynamic) 
            .Select(obj => obj.Location)
            .ToArray();

    var Cparams = new CompilerParameters( DOMref );
        Cparams.OutputAssembly = "SOURCE.DLL";

        CompilerResults res = pro.CompileAssemblyFromSource(Cparams, source);

        Assembly asm = res.CompiledAssembly;

        Type[] allTypes =  res.CompiledAssembly.GetTypes();

        foreach (Type t in allTypes)
        {
            TryLoadCompiledType(res, t.ToString());
            Debug.WriteLine(t.ToString());
        }


        /* I don't return I do something with each type here */
}

最佳答案

How do I compile C# source to memory, and create an instance of that class?

当我想将源代码作为输入并编译和执行它时,我遇到了类似的问题。这是我看完之后想出的Is it possible to dynamically compile and execute C# code fragments? :

public CompilerResults CompileSource(string sourceCode)
{
        var csc = new CSharpCodeProvider(
            new Dictionary<string, string>() { { "CompilerVersion", "v4.0" } });

        var referencedAssemblies =
                AppDomain.CurrentDomain.GetAssemblies()
                .Where(a => !a.FullName.StartsWith("mscorlib", StringComparison.InvariantCultureIgnoreCase))
                .Where(a => !a.IsDynamic) //necessary because a dynamic assembly will throw and exception when calling a.Location
                .Select(a => a.Location)
                .ToArray();

        var parameters = new CompilerParameters(
            referencedAssemblies);

        return csc.CompileAssemblyFromSource(parameters,
            sourceCode);
 }

然后我有一个辅助函数:

 public static object TryLoadCompiledType(this CompilerResults compilerResults, string typeName, params object[] constructorArgs)
    {
        if (compilerResults.Errors.HasErrors)
        {
            Log.Warn("Can not TryLoadCompiledType because CompilerResults.HasErrors");
            return null;
        }

        var type = compilerResults.CompiledAssembly.GetType(typeName);

        if (null == type)
        {
            Log.Warn("Compiled Assembly does not contain a type [" + typeName + "]");
            return null;
        }

        return Activator.CreateInstance(type, constructorArgs);
    }

所以把它放在一起

   public void Example(){
       dynamic instance = 
            CompileSource("namespace Test{public class DynamicCompile{ /*method*/}}")
            .TryLoadCompiledType("Test.DynamicCompile");

        //can now call methods on 'instance'
   }

关于C# CompilerResults GenerateInMemory?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24871955/

相关文章:

c# - "Parameter is not valid."使用保存位图时

asp.net - 在 SQL Server 2012 中创建 .NET Framework v.2.0 程序集

c# - VS从哪里获取旧的程序集引用元数据

c# - 如何将颜色名称转换为对应的十六进制表示?

c# - 如何有效地从缓存中存储和读回层次结构

asp.net-web-api - 找不到Web API : System.Net.Http版本2.0.0.0

c# - 为什么我必须链接引用程序集?

c# - 两次加载相同的程序集但版本不同

c# - 如何在 Nest ElasticSearch 客户端中编写日期范围查询?

c# - 按页复制文档以在使用 iTextsharp 之间插入空白页