C # Runtime DLL загрузка и параметры ref

Короче говоря, я хочу загрузить файл .DLL во время выполнения и попросить его изменить значение ref, которое я передал ему в качестве параметра. (.DLL будет написан на C #), но я не знаю, какой будет подходящий метод.

У меня есть класс "ALotOfData", который содержит ~ 1 гигабайт переменных.

Я хочу динамически загрузить файл .DLL, содержащий метод

"DoWork (ref ALotOfData thedata) {thedata.value = ...}

затем выполните метод, а затем выгрузите DLL и сделайте то же самое с другой DLL. (ОЧЕНЬ важно иметь возможность загружать / выгружать библиотеки DLL во время выполнения)

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

Однако это невозможно, если DLL-файлу придется принимать решение. на основе данных, какие данные следует изменить (учтите: потенциально требуется доступ ко всем данным).

Простое копирование всего пакета данных - это ... абсолютно ужасная идея, поскольку все данные имеют размер около 1 гигабайта.

Как я могу импортировать метод из .DLL динамически (во время выполнения) и передать ему параметр по ссылке, я имею в виду, фактически передать ссылку, а не просто скопировать ее? (очень важно передать реф в класс без копирования)

Псевдокод может помочь объяснить мою точку зрения:

class ALotOfData{ ... } // ~ about 1GB of initialized values inside

Main(){
DLL = loadDLL("mydll.dll");
DLL.Invoke("DoWork",ref AlotOfData); // This needs to actually change my class's contents
DLL.unload();
}

Внутри dll:

DoWork(ref ALotOfData data){
    if(data.value){
        foreach( value A in data.value ){ ... } // ~100 iterations
    }
}

Я мог бы добавить это решение в свою основную программу, но это лишило бы возможности загружать / выгружать файлы DLL.


person Alucard Pawpad    schedule 25.06.2015    source источник
comment
Не очень понятно, в чем проблема - ref не требует копирования ... Рассмотрите возможность предоставления небольшого образца кода, который показывает вашу проблему. Я подозреваю, что часть загружаемой библиотеки DLL на самом деле не важна для проблемы, и вы сможете показать ее с помощью простых классов / отражений. (пример кода для работы с ref находится в stackoverflow.com/questions/2304745/).   -  person Alexei Levenkov    schedule 25.06.2015
comment
Можете ли вы объяснить, с какой частью вашего описания у вас возникли проблемы? В C # класс - это ссылочный тип. Вы не можете передать его по значению, даже если бы захотели. Итак, вы, похоже, сосредоточились не на той части задачи. Потому что выгрузка dll после загрузки - нетривиальная задача. Для этого вам необходимо открыть свой собственный AppDomain, поскольку выгрузка сборки из AppDomain невозможна.   -  person nvoigt    schedule 25.06.2015
comment
Я почти уверен, что, поскольку вам нужно будет использовать несколько доменов приложений (поскольку вы хотите выгрузить), то, что вам нужно, не совсем возможно, поскольку экземпляры ALotOfData должны быть сериализованы через границы домена приложений, что означает вы всегда будете получать некоторую копию данных в целевой сборке.   -  person James Thorpe    schedule 25.06.2015


Ответы (3)


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

Assembly.LoadFrom("mydll.dll");

К сожалению, нет возможности выгрузить сборку. Вместо этого вам нужно выгрузить весь AppDomain, в который была загружена сборка. Обычно у вас есть один домен приложения, который содержит весь ваш работающий код; он выгружается при выходе из вашего приложения.

Если вы хотите иметь возможность выгружать сборки во время работы, вы должны создать второй AppDomain и загрузить туда свою сборку:

// Create a new domain with the dynamic assembly.
var domain = AppDomain.Create("Dynamic Assembly Domain");
domain.Load("mydll.dll");

// Do some work with the dynamic domain...

// Unload the domain.
AppDomain.Unload(domain);

Проблема здесь в том, что между доменами приложений существует граница, которую необходимо пересечь для связи с объектами в каждом домене.

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

Ответом на эту проблему является тип MarshalByRefObject :

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

Доступ к объектам MarshalByRefObject осуществляется непосредственно в границах локального домена приложения. Когда приложение в домене удаленного приложения впервые обращается к MarshalByRefObject, удаленному приложению передается прокси. Последующие вызовы прокси-сервера маршалируются обратно объекту, находящемуся в локальном домене приложения.

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

// Load your 1GB of data.
var data = ALotOfData.Load();

// Create a new domain with the dynamic assembly.
var domain = AppDomain.Create("Dynamic Assembly Domain");
domain.Load("primary.dll"); // Assembly containing your primary code.
domain.Load("mydll.dll");

// Create the remote proxy.
var remote = domine.CreateInstanceAndUnwrap("primary", "primary.RemoteControl");

// Invoke the logic in the loaded dll.
remote.Execute("mydll", "mydll.DLL", data);

// Unload the domain.
AppDomain.Unload(domain);
person Paul Turner    schedule 25.06.2015

Вы, вероятно, пришли из других языков, кроме C #.

Ключевое слово ref не делает то, что вы думаете. Класс всегда передается по ссылке. Ваш код будет работать, если вы просто удалите ключевое слово ref.

Теперь загрузка и выгрузка кода динамически выполняется с помощью Reflection. Вероятно, вам следует начать с Assembly.LoadFrom . Однако вы не можете выгрузить сборку. После загрузки он там. Что вы можете сделать, так это использовать несколько AppDomain s и удалить те, которые содержат dll. тебе не нужно.

person nvoigt    schedule 25.06.2015
comment
Обычная передача не позволяет мне редактировать данные. Это даст мне представление о классе ТОЛЬКО ДЛЯ ЧТЕНИЯ. Использование нескольких доменов приложений приведет к фактическому копированию класса между доменами приложений. - person Alucard Pawpad; 25.06.2015
comment
@AlucardPawpad Может быть, вам стоит указать, что вы знаете и чего не знаете. Судя по формулировке вашего поста, я бы не догадался, что вы вообще знали о существовании Reflection. - person nvoigt; 25.06.2015

Единственный раз, когда вам нужно передать класс с ref, - это ситуация, подобная следующей:

void DoWork(ref ALotOfData data)
{
    data = SomethingElse(); // or
    data = new ALotOfData();
}

Если вы не заменяете объект другим объектом, а вместо этого просто получаете доступ к его свойствам / методам, вам не нужен ref.

Как уже упоминалось, вам нужно будет использовать несколько доменов приложений для загрузки / выгрузки сборок. Вы должны убедиться, что ваш класс ALotOfData и любые объекты, возвращаемые из свойств / методов, наследуются от _ 4_, чтобы они не копировались между доменами приложений.

person Erik    schedule 25.06.2015