简而言之,我希望在运行时加载.DLL文件,并要求它修改我作为参数传递给它的ref值。 (.DLL将用C#编写)但我不知道合适的技术是什么。
我有一个“ALotOfData”类,其中包含大约1千兆字节的变量。
我想动态加载包含方法的.DLL文件
“DoWork(ref ALotOfData thedata){thedata.value = ...}
然后执行该方法,然后卸载DLL,并对另一个DLL执行相同的操作。 (非常重要的是能够在运行时加载/卸载DLL)
显然,解决方案是传递值本身的副本,返回修改后的副本,然后将其放回到我的类中。
但是,如果DLL文件必须做出决定,这是不可能的。基于数据,要修改哪些数据(考虑:它可能需要访问所有数据)。
仅仅复制整个数据包是一个绝对可怕的想法,因为整个数据大约是1千兆字节。
如何动态地(在运行时)从.DLL导入方法并通过ref将参数传递给它,我的意思是,实际传递引用,而不仅仅是复制它? (非常重要的是将ref传递给类,而不复制)
伪代码可能有助于解释我的观点:
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文件的目的。
答案 0 :(得分:2)
在运行时加载程序集非常简单:
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);
答案 1 :(得分:1)
您可能来自C#以外的其他语言。
ref
关键字没有按照您的想法执行。一个类始终通过引用传递。如果您刚刚完全删除ref
关键字,那么您的代码就可以使用。
现在使用Reflection动态加载和卸载代码。你应该从Assembly.LoadFrom开始。但是,您无法卸载程序集。一旦加载,它就在那里。您可以做的是使用多个AppDomain并删除那些包含您不需要的dll的内容。
答案 2 :(得分:1)
您需要使用ref
传递课程的唯一情况是:
void DoWork(ref ALotOfData data)
{
data = SomethingElse(); // or
data = new ALotOfData();
}
如果您没有用另一个对象替换该对象,而只是访问其属性/方法,那么您就不需要参考。
正如其他人所提到的,您需要使用多个AppDomain来加载/卸载程序集。您需要确保您的ALotOfData
类以及从属性/方法返回的任何对象都继承自MarshalByRefObject
,以便不会跨应用程序域复制它们。