Пример программы ниже компилирует две сборки в памяти. Первый сборник работает отлично. Второй сбой, потому что ему нужен доступ к классу из первой сборки, а тип недоступен.
В частности: член ReferencedAssemblies класса CompilerParameters является коллекцией строк и используется для загрузки манифестов сборок для получения их типов. Похоже, что компилятор C # получает типы строго из манифеста, а не с помощью отражения (возможно, из соображений производительности). В любом случае, когда сборка создается в памяти, нет файла и нет манифеста, поэтому вторая сборка сборки завершается с ошибкой как это:
ОШИБКА КОМПИЛЕРА: файл метаданных 'ax5lw0tl, версия = 0.0.0.0, культура = нейтральный, PublicKeyToken = null' не найден
Добавление обработчика событий 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 );
}
}
}