【发布时间】:2010-11-24 02:50:33
【问题描述】:
假设我有两个 C# 应用程序 - game.exe(XNA,需要支持 Xbox 360)和 editor.exe(XNA 托管在 WinForms 中) - 它们都共享一个 engine.dll 程序集,它完成了绝大多数工作。
现在假设我想添加某种基于 C# 的脚本(它不完全是“脚本”,但我会这么称呼它)。每个级别都有自己的类继承自基类(我们称之为LevelController)。
这些是这些脚本的重要限制:
它们必须是真实的、经过编译的 C# 代码
他们应该需要最少的手动“胶水”工作(如果有的话)
它们必须和其他所有东西在同一个 AppDomain 中运行
对于游戏 - 这很简单:所有脚本类都可以编译成一个程序集(例如,levels.dll),并且可以根据需要使用反射来实例化各个类。
编辑器更难。编辑器可以在编辑器窗口中“玩游戏”,然后将所有内容重置回它开始的位置(这就是编辑器首先需要了解这些脚本的原因)。
我想要实现的基本上是编辑器中的“重新加载脚本”按钮,它将重新编译和加载与正在编辑的关卡关联的脚本类,当用户按下“播放”按钮时,创建一个实例最近编译的脚本。
其结果将是编辑器内的快速编辑测试工作流程(而不是替代方法 - 保存关卡、关闭编辑器、重新编译解决方案、启动编辑器、加载关卡、测试)。
现在我认为我已经找到了一种可能的方法来实现这一点 - 这本身会导致一些问题(如下所示):
将给定级别所需的
.cs文件集合(或者,如果需要,整个levels.dll项目)编译成一个临时的、唯一命名的程序集。该程序集需要引用engine.dll。如何在运行时以这种方式调用编译器?如何让它输出这样的程序集(我可以在内存中完成)?-
加载新程序集。将具有相同名称的类加载到同一进程中是否重要? (我的印象是这些名称是由程序集名称限定的?)
现在,正如我所提到的,我不能使用 AppDomains。但是,另一方面,我不介意泄露旧版本的脚本类,因此卸载能力并不重要。除非是?我假设加载几百个程序集是可行的。
播放关卡时,实例化从刚刚加载的特定程序集的 LevelController 继承的类。如何做到这一点?
最后:
这是一种明智的做法吗?能不能做得更好?
更新:这些天我使用far simpler approach 来解决根本问题。
【问题讨论】:
-
为什么必须避免使用 AppDomains?
-
“脚本”需要操作必须在默认 AppDomain 中的对象(因为 XNA)。性能也是一个问题(至少对于 game.exe)。
-
您无法从 AppDomain 中卸载程序集,因此您可能最终会将大量“临时”程序集加载到您的 AppDomain 中。这可能会变得非常混乱,因为命名空间/类名可能是相同的!
-
@DanTup 并非如此 - 类由程序集和名称标识。只要您不意外地保留对“旧”程序集的类的引用,在您开始使用“新”程序集之后,这并不是真正的问题。
-
Russel - 这当然是可能的,只是可能“令人困惑”。某些错误(例如 InvalidCastExceptions)可能不会显示程序集名称,因此您最终会遇到有趣的“无法将 Namespace1.Class1 转换为 Namespace1.Class1”错误。当然有可能!
标签: c# compilation assembly.load