2009-01-18 31 views
4

下面的示例程序编译了两个内存中的程序集。第一次编译正常。第二个失败,因为它需要访问第一个程序集中的类,并且该类型不可用。在C#中,你如何从另一个内存组件中引用类型?

具体而言:CompilerParameters类的ReferencedAssemblies成员是一个字符串集合,它用于加载程序集的清单以获取它们的类型。看来C#编译器严格从清单中获取类型,而不是通过使用反射(可能出于性能原因)。无论如何,当在内存中构造程序集时,没有文件并且没有清单,因此第二个程序集构建失败并出现错误像这样:

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

添加一个AssemblyResolver事件处理程序不起作用。我试过这个,它看起来并没有被调用过。从我所能告诉的(我是一个新手,用.NET来承担我)编译器只关心清单;它实际上并未尝试在此时加载程序集,因此AssemblyResolver不在图片中。

我可以,如果不顾一切,在磁盘上构建我的程序集,这将解决直接的问题,有一个物理的dll和清单读取。我宁愿不这样做,因为它导致必须管理将成为磁盘上临时程序集的大量集合。

我很乐观.net可以做到这一点,作为新手,我只是想念它。我希望代码示例中的间距可以正常显示,它似乎可以在预览窗口中正确显示一段时间,但是一旦语法高亮显示完成,它就会重新渲染并且间距不正确,但它仍然可读。

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); 
     } 
    } 
    } 

回答

3

根据我在MSDN上找到的文档以及我查看的反射器中的代码(对于编译器类),无法做到您想要的。原因在于,在下面,您正在使用的代码编译器类会被释放到实际的编译器中。

此外,代码编译器类实际上是在下面生成临时文件,并基于我在反射器中查看的代码,它们没有清理文件。所以基于此,我会说只是在临时位置的磁盘上生成文件,然后添加引用。

+0

.Net编译器似乎没有留下临时程序集。它可能会删除它们(请参阅TempFileCollection。) 我已经通过编译磁盘上的程序集来运行代码。我必须咬紧牙关并管理这些临时文件,但至少它可以工作。 – 2009-01-18 06:36:00

1

定义普通程序集中的接口,并在每个生成的程序集中都有类实现这些接口。生成的程序集将需要引用包含接口的引用,而不是彼此。

+0

当我可以提前预测所需的类型(或接口)时,这很好。在目前的情况下,我需要一个更一般的解决方案来创建动态类型(通常只生成一次)并且无法事先预测。 – 2009-01-18 03:41:11

相关问题