Перезагрузка классов в C# - странное поведение

Вступление

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

public interface ICustomCommand
{
    string GetAuthor(); //get author, for good measure
    string GetFunctionName(); //get name, to differentiate from other mods
    void Execute(CrplCore core); //actually do the work
}

Пример:

public class HelloCommandExample : ICustomCommand
{
    public void Execute(CrplCore core)
    {
        DebugText.LogMessage(core, "Hello mods: " + core.GetStringFromStack());
        Debug.Log("HelloCommandExample"); 
    }

    public string GetAuthor()
    {
        return "kajacx";
    }

    public string GetFunctionName()
    {
        return "HelloTrace";
    }
}

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

Однако после такой загрузки моды не могут быть перезагружены, даже если я изменю DLL-файл и снова запустите скрипт импорта. Вот как выглядит скрипт импорта:

private static void DoInit()
{
    avaliableCommands = new Dictionary<string, ICustomCommand>(); //static field

    string[] files = Directory.GetFiles(@"ParticleFleet_Data\Managed\mods");

    foreach (string file in files) //list all files in the mods directory
    {
        if (!file.ToLower().EndsWith(".dll"))
        {
            continue; //skip all non-dll files
        }
        try
        {
            byte[] bytes = File.ReadAllBytes(file);

            string hash;
            using (SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider())
            {
                hash = Convert.ToBase64String(sha1.ComputeHash(bytes));
            }
            Debug.LogFormat("Loading file {0}, hash: {1}", file, hash);

            //load the .dll file as and assembly
            Assembly assembly = Assembly.Load(bytes);
            Type[] types = assembly.GetExportedTypes();
            foreach (Type type in types) //list all classes from the library
            {
                if (typeof(ICustomCommand).IsAssignableFrom(type))
                {
                    //add new command from the library
                    ICustomCommand command = (ICustomCommand)assembly.CreateInstance(type.FullName);
                    AddCommand(command); //this adds the command into the avaliableCommands field
                    Debug.LogFormat("Custom command {0} by {1} loaded, usage: {2}",
                        command.GetFunctionName(), command.GetAuthor(), command.GetFunctionSignature());
                }
            }
        }
        catch (Exception ex)
        {
            Debug.LogException(ex);
        }
    }

    Debug.Log("Total mod commands loaded: " + avaliableCommands.Count);
}

Эта проблема

Теперь, наконец, мы подошли к интересной части: что работает / не работает в этом скрипте импорта:

  • Сначала запустить правильно импортировать все классы из всех файлов
  • При любом последующем запуске будут импортированы точно такие же ошибки, даже если файл.dll изменяется с новым содержимым (проверено с помощью хэш-журнала SHA1)
  • Перезапуск игры после этого сделает эти изменения видимыми
  • Удаление DLL-файла приведет к тому, что классы не загрузятся
  • Удаление классов из файла.dll (перестройка.dll без / разных классов) не приведет к каким-либо изменениям - старые классы будут по-прежнему загружаться так, как если бы DLL не изменялся вообще.
  • Переименование измененного.dll не помогает, старые классы по-прежнему загружаются
  • Добавление новых классов в.dll не работает, только старые классы будут загружены
  • Изменение пространств имен классов внутри.dll не работает
  • Переименование классов внутри.dll не работает
  • Однако изменение содержимого.dll действительно меняет хеш.

Посмотрев этот список, я могу прийти только к одному выводу: внутри DLL есть какой-то скрытый идентификатор, и загруженная сборка использует его для кэширования файлов DLL. Этот идентификатор, вероятно, похож на имя (с путем) проекта Visual Studio, в котором я создал библиотеку.dll.

Вопрос

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

Я буду более чем рад изменять пространство имен классов каждый раз, когда сохраняю их, если возникнет другая проблема с загрузкой одного и того же класса несколько раз, но сейчас проблема в том, что Assembly.Load загружает одну и ту же сборку снова и снова, независимо от того, какие данные я в нее добавляю.

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

0 ответов

Другие вопросы по тегам