在C#中,你如何从一个内存中的程序集引用类型在另一个? [英] In C#, how do you reference types from one in-memory assembly inside another?

查看:152
本文介绍了在C#中,你如何从一个内存中的程序集引用类型在另一个?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

该示例程序编译如下两个内存组件。第一次编译工作正常。第二个失败,因为它需要来自第一组件访问一个类和类型不可

具体地:所述CompilerParameters类的ReferencedAssemblies构件是一个字符串集合,它是用来加载组件的清单,以获得它们的类型。看来C#编译器获取各类严格的清单,而不是通过使用反射(可能是出于性能的考虑。)在任何情况下,当一个程序集在内存中构造没有文件,没有清单,以便在第二组件构建失败,错误像这样的:

编译器错误:元数据文件ax5lw0tl,版本= 0.0.0.0,文化=中性公钥=空'找不到

添加AssemblyResolver事件处理程序不起作用。我想这和它看起来还没有被调用。从我可以告诉(我就是一个新手,净如此忍受我)编译器只关心清单;它实际上并不试图加载组件在这个时候,所以AssemblyResolver是不是在图片

我,如果逼急了,我的构建组件在磁盘上这将解决眼前的问题有一个物理DLL和舱单阅读。我宁愿没有这样做,因为它会导致其管理究竟会成为一个非常大的集合在磁盘上的临时装配。

我很乐观净能做到这一点,并作为一个新手,我只是失踪了。

(我希望的间距来在code样品进行确定。它似乎在preVIEW窗口一会儿正确呈现,但一旦语法高亮完成它重新渲染和间距不正确,虽然它仍然具有可读性。)

 使用系统;
使用系统codeDom.Compiler。
使用的System.Reflection;
使用System.Collections.Generic;
使用Microsoft.CSharp;

命名空间AsmCompileTest
  {
  类节目
    {
    静态编译大会(字符串code,汇编referencedAssembly)
      {
      CompilerParameters CP =新CompilerParameters();
      cp.GenerateExecutable = FALSE;
      cp.GenerateInMemory = TRUE;

      如果(NULL!= referencedAssembly)
        {
        cp.ReferencedAssemblies.Add(referencedAssembly.FullName);
        }

      codeDomProvider商=新CSHARP codeProvider(新字典<字符串,字符串> {{CompilerVersion,V3.5}});

      CompilerResults compilerResults = provider.CompileAssemblyFromSource(CP,code);

      如果(compilerResults.Errors.HasErrors)
        {
        的foreach(在compilerResults.Errors CompilerError错误)
          {
          Console.WriteLine(编译器错误:+ error.ErrorText);
          }
        }

      返回compilerResults.CompiledAssembly;
      }


    静态字符串code1 =使用系统; +
                          公共类HelloClass+
                          {+
                          公共HelloClass(){Console.WriteLine(\你好,世界!\);}+
                          };


    静态字符串code2 =使用系统; +
                          公共类识别TestClass+
                          {+
                          公众识别TestClass(){新HelloClass();}+
                          };

    静态无效的主要()
      {
      大会ASM1 =编译(code1,NULL);
      Console.WriteLine(编译:+ asm1.FullName);

      asm1.GetType(HelloClass).InvokeMember(的String.Empty,BindingFlags.CreateInstance,NULL,NULL,NULL);

      大会ASM2 =编译(code2,ASM1);
      Console.WriteLine(编译:+ asm2.FullName);

      asm2.GetType(TestClass中).InvokeMember(的String.Empty,BindingFlags.CreateInstance,NULL,NULL,NULL);
      }
    }
  }
 

解决方案

根据文件发现<一href="http://msdn.microsoft.com/en-us/library/system.$c$cdom.compiler.compilerparameters%28VS.80%29.aspx"相对=nofollow> MSDN 并在code的反射,我看了(编译器类),这是不可能做到你想要什么。其原因是下面,您正在使用掏出实际编译器code编译器类。

另外,code编译类实际上是生成临时文件的下面,并基于code口在反射看着,它们不清理的文件。因此,基于这一点,我想说的只是生成磁盘的临时位置上的文件,然后添加引用。

The example program below compiles two in-memory assemblies. The first compilation works fine. The second one fails because it needs access to a class from the first assembly and the type isn't available.

Specifically: The ReferencedAssemblies member of the CompilerParameters class is a string collection and it is used to load the manifests of the assemblies to obtain their types. It appears the C# compiler gets types strictly from the manifest rather than by using reflection (possibly for performance reasons.) In any case, when an assembly is constructed in memory there is no file and no manifest so the second assembly build fails with an error like this:

COMPILER ERROR: Metadata file 'ax5lw0tl, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' could not be found

Adding an AssemblyResolver event handler doesn't work. I tried this and it looks like it isn't ever called. From what I can tell (and I'm a novice with .Net so bear with me) the compiler only cares about the manifest; it's not actually trying to load the assembly at this time, so AssemblyResolver isn't in the picture.

I could, if desperate, construct my assemblies on disk which would solve the immediate problem to have a physical dll and manifest to read. I would much rather not do this as it leads to having to manage what will become a very large collection of temporary assemblies on disk.

I'm optimistic .Net can do this and, being a novice, I'm simply missing it.

(I hope the spacing comes out ok on the code sample. It seems to render properly in the preview window for a few moments but once the syntax highlighter is done it rerenders and the spacing is incorrect although it remains readable.)

using System;
using System.CodeDom.Compiler;
using System.Reflection;
using System.Collections.Generic;
using Microsoft.CSharp;  

namespace AsmCompileTest
  {
  class Program
    {
    static Assembly Compile( string code, Assembly referencedAssembly )
      {
      CompilerParameters cp = new CompilerParameters();
      cp.GenerateExecutable = false;
      cp.GenerateInMemory = true;

      if( null != referencedAssembly )
        {
        cp.ReferencedAssemblies.Add( referencedAssembly.FullName );
        }

      CodeDomProvider provider = new CSharpCodeProvider( new Dictionary<string,string> { { "CompilerVersion", "v3.5" } } );

      CompilerResults compilerResults = provider.CompileAssemblyFromSource( cp, code );

      if( compilerResults.Errors.HasErrors )
        {
        foreach( CompilerError error in compilerResults.Errors )
          {
          Console.WriteLine( "COMPILER ERROR: " + error.ErrorText );
          }
        }

      return compilerResults.CompiledAssembly;
      }


    static string Code1 = "using System;" +
                          "public class HelloClass" +
                          "  {" +
                          "  public HelloClass() { Console.WriteLine( \"Hello, World!\" ); }" +
                          "  }";


    static string Code2 = "using System;" +
                          "public class TestClass" +
                          "  {" +
                          "  public TestClass() { new HelloClass(); }" +
                          "  }";

    static void Main()
      {
      Assembly asm1 = Compile( Code1, null );
      Console.WriteLine( "Compiled: " + asm1.FullName );  

      asm1.GetType( "HelloClass" ).InvokeMember( String.Empty, BindingFlags.CreateInstance, null, null, null );  

      Assembly asm2 = Compile( Code2, asm1 );
      Console.WriteLine( "Compiled: " + asm2.FullName );  

      asm2.GetType( "TestClass" ).InvokeMember( String.Empty, BindingFlags.CreateInstance, null, null, null );
      }
    }
  }

解决方案

Based on documentation found on MSDN and on the code in reflector that I looked at (for the compiler classes) it is not possible to do what you want. The reason is that underneath, the code compiler classes that you are using shell out to the actual compiler.

Also, the code compiler classes are actually generating the temporary files underneath, and based on the code I looked at in reflector, they are not cleaning up the files. So based on that, I would say just generate the file on the disk in a temporary location, and then add reference to it.

这篇关于在C#中,你如何从一个内存中的程序集引用类型在另一个?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆