AppDomain.TypeResolve не вызывается для динамического типа - PullRequest
0 голосов
/ 09 июля 2019

Demorepo :

https://github.com/gabbersepp/csharp-dynamic-replace-class

Как использовать :

  1. Оформить заказ
  2. Компилировать
  3. Удалить TestLib.dll и TestLib.pdb из консоли / bin / Debug
  4. Выполнить console.exe через cmd

Старый пост SO:

Заменить существующее определение класса во время выполнения на вновь созданный тип

Задано :

Класс вlib:

namespace Test.TestLib
{
    public class Class1
    {
    }
}

И второй класс, который создает его экземпляр:

namespace console
{
    public class AnotherClass
    {
        public void Create()
        {
            new Class1();
        }
    }
}

И консольное приложение, которое вызывает create:

    static void Main(string[] args)
    {
        //...
        new AnotherClass().Create();
    }

Пожалуйста, имейте в виду, что только Class1 находится в дополнительной библиотеке.Два других класса находятся в одном и том же.

Что я хочу сделать :

Заменить сборку во время выполнения:

        AssemblyName dynamicAssemblyName = new AssemblyName("TestLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
        dynamicAssembly =
            AssemblyBuilder.DefineDynamicAssembly(dynamicAssemblyName, AssemblyBuilderAccess.Run);
        var dynamicModule = dynamicAssembly.DefineDynamicModule("TestLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");

Но яхочу не указывать тип на данный момент.Вместо этого я использую:

AppDomain.CurrentDomain.TypeResolve += CurrentDomain_TypeResolve;

    private static Assembly CurrentDomain_TypeResolve(object sender, ResolveEventArgs args)
    {
        Console.WriteLine("resolve type");
        if (args.Name.Contains("TestLib"))
        {
            dynamicModule.DefineType("Test.TestLib.Class1", TypeAttributes.Class | TypeAttributes.Public).CreateType();
            return dynamicAssembly;
        }

        return null;
    }

Проблема :

Событие не вызывается при выполнении строки new AnotherClass().Create();.Вместо этого выдается исключение:

System.TypeLoadException: Der Тип "Test.TestLib.Class1" в сборке "TestLib, версия = 1.0.0.0, культура = нейтральная, PublicKeyToken = null" konnte nichtgeladen werden

что-то вроде:

System.TypeLoadException: тип "Test.TestLib.Class1" в сборке "TestLib, версия = 1.0.0.0, Culture =нейтральный, PublicKeyToken = null "не может быть загружен

Пожалуйста, посмотрите репозиторий для полного примера.

// Edit: Демонстрационный проектнаписано с VS2019 für .net461.Я думаю, что основные понятия одинаковы для ядра dotnet.Дайте мне знать, если вы предпочитаете работать с ядром dotnet, чтобы я мог предоставить проект для обеих платформ.

// Edit2:

Я отладил в коде ILи увидел, что все работает нормально, пока не будет вызван конструктор Class1: enter image description here Так что лично я не думаю, что обработчик событий подключен слишком поздно, как заявлено Бруно.

Официальная документация гласит, что это событие вызывается, если сборка неизвестна:

https://docs.microsoft.com/de-de/dotnet/api/system.appdomain.typeresolve?view=netframework-4.8 Событие TypeResolve наступает, когда общеязыковая среда выполнения не может определить сборку, котораяможно создать запрошенный тип

Я раньше этого не читал.Надеюсь, кто-нибудь может мне помочь: -)

// Edit3 - возможное решение :

Обходным путем может быть создание типов на основе списка имен классов.Чтобы не потерять безопасность компиляции, я могу использовать nameof, который не производит код IL.Пример можно найти в репо в ветке resolveType-solution1.Но, конечно, не решение, которое я ищу: - (

Ответы [ 2 ]

1 голос
/ 09 июля 2019

Я не уверен на 100%, но думаю, что понял, что происходит.

При выполнении вашего кода ваш main оценивается JIT перед выполнением. Это приводит к попытке загрузки AnotherClass и его зависимостей (поскольку в конце концов все очень вероятно будет встроено, потому что оно очень маленькое).

Это означает, что у вас не будет возможности запустить этот код до того, как .net попытается найти ваш тип:

AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
AppDomain.CurrentDomain.TypeResolve += CurrentDomain_TypeResolve;

Мне удалось обойти это ограничение, не упоминая класс в вашем основном, заменив:

new AnotherClass().Create();

по:

var type = Type.GetType("AnotherClass");
var method = type.GetMethod("Create");
var instance = type.GetConstructor(new Type[] { }).Invoke(new object[0]);
method.Invoke(instance, new object[0]);

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

Заберите: вы не знаете, когда будет загружен ваш тип (на самом деле, как только JIT читает код, упоминающий ваш тип). Это означает, что вы должны подключить свой обратный вызов как можно скорее и максимально исключить включение кода с указанием вашего типа.

0 голосов
/ 10 июля 2019

вызов Type.GetType("Test.TestLib.Class1"); вызовет AppDomain.TypeResolve, но не AppDomain.AssemblyResolve, хотя и не знаю, почему

...