Товарищи, кратко опишу ситуацию:
Пишу небольшое расширяемое приложение. Чтобы пользователи могли добавлять собственный функционал, в рамках программы создал интерфейсы, от которых они должны наследовать свои типы
При этом я сделал генерацию кода также и внутри самого приложения для большего удобства
Положим, есть такой интерфейс:
namespace MyAppNamespace
{
public interface INamed
{
string Name { get; }
}
}
Далее внутри приложения генерируется следующий код (и кладется в resultCode):
using MyAppNamespace;
public class Wrapper : INamed
{
public string Name { get { return "Test"; } }
}
Компилирую это дело:
// Указываю, что на выходе мне не нужен исполняемый файл, а также что сборку нужно создать по указанному пути
CompilerParameters options = new CompilerParameters { GenerateExecutable = false, GenerateInMemory = false, OutputAssembly = $"{SavePath}.dll" };
options.ReferencedAssemblies.Add(new Uri(GetType().Assembly.CodeBase, UriKind.Absolute).LocalPath); // Добавляю ссылку на текущую сборку для наследования интерфейса
// Получаю результат компиляции
CompilerResults results = new Microsoft.CSharp.CSharpCodeProvider().CompileAssemblyFromSource(options, resultCode);
// Опустим проверки
// Загружаю сборку из массива байт (так как сам файл потом, возможно, может быть удален)
Assembly asm = Assembly.Load(File.ReadAllBytes(results.PathToAssembly));
// Создаю instance типа, который в сборке унаследован от нужного интерфейса
INamed named = (INamed)Activator.CreateInstance(asm.DefinedTypes.First(x => x.ImplementedInterfaces.Contains(typeof(INamed))).AsType());
asm = null;
GC.Collect(); // Вычищаю сборку из памяти. По крайней мере, я хочу в это верить
return named;
После этого я могу спокойно получать доступ к named.Name.
Через некоторое время объект "выбрасывается", пока пользователь явно не укажет, что хочет его использовать. В таком случае его нужно будет повторно достать из сборки
Но есть одно жирное но: если я попытаюсь тем же самым образом загрузить сборку и достать из нее тип во второй раз, то визуально процесс пройдет успешно, но при попытке доступа к named.Name вылетит ошибка, что сборка, в которой он определен, не найдена
Я могу поправить логику приложения и сделать считывание единоразовым (пожалуй, это будет даже правильнее), но сейчас для меня важно понимание, почему же так происходит: при первом считывании все работает как часы, а при втором считывании тем же самым способом из того же самого файла процесс проходит успешно, но объект оказывается "битым", так как при попытке доступа к его свойствам я получу ошибку о том, что сборка не может быть загружена
Ответ
Вычищаю сборку из памяти. По крайней мере, я хочу в это верить
Увы, эта вера не имеет оснований. Использованный вами способ загрузки сборки не только не позволяет выгрузить сборку из памяти без выгрузки всего домена приложений, но и при каждом повторном запуске будет грузить сборку с того же пути заново (иными словами, это хороший способ исчерпать память при длительной работе программы).
Создайте Dictionary
Комментариев нет:
Отправить комментарий