简而言之,我希望加载一个。DLL文件,并要求它修改我作为参数传递给它的ref值。(.DLL将用C#编写),但我不知道什么是合适的技术。
我有一个类"ALotOfData",其中包含价值约1千兆字节的变量
我想动态加载。包含方法的DLL文件
"DoWork(ref ALotOfData thedata){thedata.value=…}
然后执行该方法,然后卸载DLL,并对另一个DLL执行同样的操作。(具有在运行时加载/卸载DLL的能力非常重要)
显然,解决方案是传递值本身的副本,返回修改后的副本,并将其放回我的类中。
但是,如果DLL文件必须做出决定,这是不可能的。基于数据,修改哪些数据(考虑:它可能需要访问所有数据)。
仅仅复制整个数据包是……一个绝对可怕的想法,因为整个数据大约有1 GB大。
如何从导入方法。DLL动态(在运行时)并通过ref向其传递参数,我的意思是,实际上传递了一个引用,而不仅仅是复制它?(在不复制的情况下将ref传递给类非常重要)
psuedo代码可能有助于解释我的观点:
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文件的目的。
在运行时加载程序集非常容易:
Assembly.LoadFrom("mydll.dll");
不幸的是,无法卸载程序集。相反,您必须卸载程序集加载到的整个AppDomain。通常情况下,您只有一个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);
这里的问题是AppDomains之间有一个边界,必须跨越这个边界才能与每个域中的对象通信。
问题(正如您所知道的)是,域之间通信的默认机制是创建对象的副本。会生成一个完整的克隆并将其传递到另一个域,这在处理大量信息时是不合适的。
这个问题的答案是类型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);
您可能来自C#之外的其他语言。
ref
关键字的作用与您认为的不同。类总是通过引用传递。如果您只是完全删除了ref
关键字,那么您的代码就可以工作了。
现在,动态加载和卸载代码是使用反射完成的。你可能应该从Assembly开始。LoadFrom。但是,不能卸载程序集。一旦装载,它就在那里。您可以使用多个AppDomain,并删除那些包含您不需要的dll的AppDomain。
您唯一需要传递带有ref
的类的情况如下:
void DoWork(ref ALotOfData data)
{
data = SomethingElse(); // or
data = new ALotOfData();
}
如果你没有用另一个对象替换对象,而只是访问它的属性/方法,那么你就不需要引用
正如其他人所提到的,您将需要使用多个AppDomain来加载/卸载程序集。您需要确保ALotOfData
类和从属性/方法返回的任何对象继承自MarshalByRefObject
,这样它们就不会跨应用程序域复制。