2009-01-18 23 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

相關問題