Как применить XmlIncludeAttribute к TypeBuilder?

Я разрабатываю библиотеку на С#, которая генерирует типы выполнения, используя класс System.Reflection.Emit.TypeBuilder и я хочу сгенерировать следующую иерархию классов:

[XmlInclude(typeof(Derived))]
public class Base
{
}

public class Derived : Base
{
}

Я использую класс TypeBuilder следующим образом:

class Program
{
    public static void Main(string[] args)
    {
        var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("Test"), AssemblyBuilderAccess.Run);

        var moduleBuilder = assembly.DefineDynamicModule("Test");

        var baseTypeBuilder = moduleBuilder.DefineType("Base", TypeAttributes.Public, typeof(Object));

        var derivedTypeBuilder = moduleBuilder.DefineType("Derived", TypeAttributes.Public);

        derivedTypeBuilder.SetParent(baseTypeBuilder);

        baseTypeBuilder.SetCustomAttribute(new CustomAttributeBuilder(typeof(XmlIncludeAttribute).GetConstructor(new[] { typeof(Type) }), new[] { derivedTypeBuilder }));

        var baseType = baseTypeBuilder.CreateType();

        var derivedType = derivedTypeBuilder.CreateType();

        var attribute = baseType.GetCustomAttribute<XmlIncludeAttribute>();
    }
}

Вызов:

var attribute = baseType.GetCustomAttribute<XmlIncludeAttribute>();

Я получаю следующую ошибку:

Не удалось загрузить файл или сборку "Test, Version = 0.0.0.0, Culture = neutral, PublicKeyToken = null" или одна из его зависимостей. Система не может найти указанный файл.

Любые идеи хорошо оценены: как я могу применить настраиваемый атрибут для TypeBuilder для базового класса, который ссылается на TypeBuilder для производного класса?

PS: Я использую Visual Studio 2017 (v15.7.5) и библиотеку классов С# (шаблон проекта.NET Framework). NET.NET Core или.NET Standard

Ответ 1

Невозможно предоставить много оснований, кроме решения, которое будет работать без дополнительной загрузки/выгрузки или с диска:

Добавление отдельного поля, содержащего фактическую Assembly, можно просто подписаться на AppDomain.CurrentDomain.AssemblyResolve и проверить, если динамическая сборка была запрошена.

Если это так, вам просто нужно вернуть свое поле, и он отлично работает.

Пример:

class Program
{
    static Assembly ass;
    public static void Main(string[] args)
    {
        AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
        var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("Test"), AssemblyBuilderAccess.Run);

        var moduleBuilder = assembly.DefineDynamicModule("Test");

        var baseTypeBuilder = moduleBuilder.DefineType("Base", TypeAttributes.Public, typeof(Object));

        var derivedTypeBuilder = moduleBuilder.DefineType("Derived", TypeAttributes.Public);

        derivedTypeBuilder.SetParent(baseTypeBuilder);

        baseTypeBuilder.SetCustomAttribute(new CustomAttributeBuilder(typeof(XmlIncludeAttribute).GetConstructor(new[] { typeof(Type) }), new[] { derivedTypeBuilder }));

        var baseType = baseTypeBuilder.CreateType();

        var derivedType = derivedTypeBuilder.CreateType();
        ass = baseType.Assembly;

        var attribute = baseType.GetCustomAttribute<XmlIncludeAttribute>();

        Console.WriteLine(attribute.Type.FullName);
    }

    private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
    {
        return ass;
    }
}

Ответ 2

Я воспроизвел ваше исключение. Похоже, что.NET Framework хочет читать модуль с диска.

Самое простое решение проблемы - сохранить сборку на диске:

var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(
  new AssemblyName("Test"), 
  AssemblyBuilderAccess.RunAndSave); // allow run & save

var moduleBuilder = assembly.DefineDynamicModule("Test",
  "Test.dll"); // specify a file name where module will be stored

...

var baseType = baseTypeBuilder.CreateType();
var derivedType = derivedTypeBuilder.CreateType();

assembly.Save("Test.dll");

Теперь я смог получить атрибут без исключения:

var attribute = baseType.GetCustomAttribute<XmlIncludeAttribute>();

Ответ 3

Хорошо, я могу сказать вам, почему исправление @X39 опубликовано. Пройдя через лес кода рамки (возможно, резервные копии для разных культур) для baseType.GetCustomAttribute(); вызовите, когда, наконец, достигните appdomain.cs Я вижу:

enter image description here

На этом этапе _AssemblyResolve имеет значение null (которое является частным полем поддержки для AppDomain.CurrentDomain.AssemblyResolve), и после перехода на нуль, код выходит из строя с сообщенной ошибкой.

Дополнительная информация: https://docs.microsoft.com/en-us/dotnet/api/system.appdomain.assemblyresolve?view=netframework-4.7.2

@X39 не стесняйтесь сливать мой ответ и дайте мне знать, чтобы я мог удалить мой, я не хотел напрямую редактировать ваш ответ.