Отражение C # - Как перезагрузить класс во время выполнения? - PullRequest
1 голос
/ 03 апреля 2019

В настоящее время я работаю над проектом на C #, где мне нужно реализовать рефлексию.Я создал приложение WPF с графическим интерфейсом.Этот GUI содержит комбинированный список, который содержит все имена классов, которые реализуют определенный интерфейс.Классы с отображаемыми именами классов живут в одном и том же решении.Рядом с выпадающим списком находится кнопка для обновления содержимого в выпадающем списке.Однако, когда я запускаю свое приложение, изменяю имя класса, который реализует интерфейс, и нажимаю на эту кнопку обновления, изменения не отображаются в выпадающем списке.Например, когда я меняю имя класса, вместо старого должно отображаться новое имя класса.

Я извлек эту часть своего проекта, чтобы протестировать его в пустом консольном приложении.Здесь у меня есть интерфейс, который реализуется классами QuickSortAlgorithm, DynamicSortAlgorithm и MergeSortAlgorithm.Затем я написал следующий простой код в своем основном классе.

    public static List<string> AlgoList = new List<string>();

    static void Main(string[] args) {
        RefreshAlgorithms();
        Print();

        Console.WriteLine("\nChange a classname and press a key \n");
        Console.ReadKey();

        Print();

        Console.WriteLine("\nPress a key to exit the program \n");
        Console.ReadKey();
    }

    private static void RefreshAlgorithms() {
        AlgoList.Clear();
        Type AlgorithmTypes = typeof(IAlgorithms);
        foreach (var type in Assembly.GetCallingAssembly().GetTypes()) {
            if (AlgorithmTypes.IsAssignableFrom(type) && (type != AlgorithmTypes)) {
                AlgoList.Add(type.Name);
            }
        }
    }

    private static void Print() {
        Console.WriteLine("Algorithm classes:");
        foreach (var Algo in AlgoList) {
            Console.WriteLine(Algo);
        }
    }

Когда я запускаю приложение, вижу напечатанные имена классов QuickSortAlgorithm, DynamicSortAlgorithm и MergeSortAlgorithm.Однако, если я изменю имя, например, класса QuickSortAlgorithm на QuickSortAlgorithmmmmm, я ожидаю, что он напечатает QuickSortAlgorithmmmmm, как только я нажму клавишу.Однако это не тот случай, и имя QuickSortAlgorithm по-прежнему отображается.

У меня такое ощущение, что я что-то упускаю из виду в концепции отражения.Можно ли это сделать даже после построения решения?Если я правильно понял, эта концепция позволяет вносить изменения во время выполнения.Я знаю, что это сделает мое приложение намного медленнее, но я действительно хочу узнать больше об этой концепции.Если кто-нибудь объяснит мне, что я делаю неправильно, я был бы очень счастлив.

Ответы [ 3 ]

1 голос
/ 03 апреля 2019

Это, к сожалению, не работает.Когда ваша сборка будет загружена, она останется загруженной как есть, изменения будут применяться только при перезапуске приложения.

Если вы используете .NET Framework, вы можете создать новый домен приложений и загрузить свою сборку в этот домен приложений.Когда вы закончите, вы можете выгрузить AppDomain и вместе с ним свою сборку.Это можно делать несколько раз в работающем приложении.

void RefreshAlgorithms()
{
    var appDomain = AppDomain.CreateDomain("TempDomain");
    appDomain.Load(YourAssembly);
    appDomain.DoCallback(Inspect);
    AppDomain.Unload(appDomain);
}

void Inspect()
{
    // This runs in the new appdomain where you can inspect your classes
}

Будьте осторожны, поскольку при работе с доменами приложений есть предостережения, такие как необходимость использовать удаленное взаимодействие при взаимодействии с доменом приложений.

В .NET Core такого способа нет, насколько я знаю

1 голос
/ 03 апреля 2019

Как только вы загрузите скомпилированную сборку .NET в ваше приложение, вы не сможете вносить дальнейшие изменения в типы в этой сборке без перезапуска и перестройки приложения. Если бы это было разрешено, то это могло бы привести ко всем видам странного поведения. Например, представьте, что в приложении было List<Foo>, заполненное 3 foos, а затем Foo.Id было изменено с int на string. Что должно случиться с этими живыми данными?

Однако, если ваше приложение, выполняющее отражение, отличается от отражаемой сборки, можно настроить все так, чтобы вы могли следить за изменениями в этом файле сборки и заново выполнять свое отражение. Ключ должен отказаться от System.Reflection (которая работает только на загруженных сборках) и вместо этого использовать библиотеку Mono.Cecil .

Сесил читает метаданные сборки, не загружая код в приложение, поэтому он хорошо работает для варианта использования «только отражение». Конечно, на самом деле он не может вызвать код. API Cecil имеет много общего с System.Reflection. Например:

var assembly = Mono.Cecil.AssemblyDefinition.ReadAssembly(Path.Combine(projectDirectory, "bin", "Debug", "Something.dll"));
var controllerTypes = assembly.MainModule.Types.Where(t => t.BaseType?.FullName == "System.Web.Mvc.Controller")
    .ToArray();

Еще одно замечание: в .NET Framework (не .NET Core) содержится понятие доменов приложений, которые можно загружать в незагруженном виде. Они действуют как «подпроцессы» .NET внутри одного процесса и имеют правила о том, что может выходить за их границы. Если вам действительно нужно как перезагрузить код, так и выполнить его, это может быть решением.

Другим вариантом может быть API сценариев Roslyn, который будет хорошо работать, если вы хотите динамически загружать и выполнять исходный код (по сравнению со скомпилированными сборками).

0 голосов
/ 03 апреля 2019

Похоже, вы упускаете один маленький шаг: создание кода.После того, как вы переименуете класс в QuickSortAlgorithmmmm, вам нужно сохранить и собрать эту сборку.

Это позволит воссоздать сборку (если ваше приложение не имеет открытого дескриптора).После этого при нажатии кнопки «Обновить» должно появиться новое имя.

Если вы не можете перезагрузить сборку, поскольку в ней тоже есть код GUI (который запущен), вы можете выделить классы.которые реализуют интерфейс в своей собственной сборке, потенциально собирают его отдельно и копируют в каталог, где ваше приложение может его найти (например, в каталоге Plugins).

...