В C # как вы ссылаетесь на типы из одной сборки в памяти внутри другой? - PullRequest
4 голосов
/ 18 января 2009

Пример программы ниже компилирует две сборки в памяти. Первый сборник работает отлично. Второй сбой, потому что ему нужен доступ к классу из первой сборки, а тип недоступен.

В частности: член 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 );
      }
    }
  }

Ответы [ 2 ]

3 голосов
/ 18 января 2009

На основании документации, найденной на MSDN и кода в отражателе, который я посмотрел (для классов компилятора), невозможно сделать то, что вы хотите. Причина в том, что внизу классы компилятора кода, которые вы используете, распространяются на фактический компилятор.

Кроме того, классы компилятора кода фактически генерируют временные файлы под ними, и, основываясь на коде, который я посмотрел в отражателе, они не очищают файлы. Исходя из этого, я бы сказал, просто сгенерировать файл на диске во временной папке, а затем добавить ссылку на него.

1 голос
/ 18 января 2009

Определить интерфейсы в обычной сборке и иметь классы в каждой сгенерированной сборке для реализации этих интерфейсов. Для сгенерированных сборок потребуется ссылка на ту, которая содержит интерфейсы, а не друг на друга.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...