我有兴趣创建一个由模块组成的桌面应用程序,这些模块的源代码嵌入在应用程序本身中,允许用户在运行它时编辑应用程序。在不重新启动应用程序的情况下使用更新的模块。任何人都可以为此建议一个好的架构吗?
我希望使用Microsoft.Net和C#。 DLR不是一种选择。
谢谢!
答案 0 :(得分:2)
在短篇文章中建议一个好的架构并不容易。
首先,我定义了用户编写/修改的每个模块必须实现的契约(接口)。它应该至少包含一个Execute方法。
然后我会为这些模块创建一个Wrapper-Class:
然后我会有一些包含所有模块包装器集合的shell。然后,任何成功编译的包装器都会让Shell调用模块接口的Execute方法。
在动态编译和执行代码时,此链接应提供您需要的所有信息: http://www.west-wind.com/presentations/dynamicCode/DynamicCode.htm
答案 1 :(得分:0)
嗯,动态语言肯定是最合适的......
您可以使用System.Reflection.Emit命名空间中的类型动态创建程序集。
然而,会非常痛苦,因为您需要将这些动态程序集加载到自定义AppDomains中,否则您将无法再次卸载它们。
这再次意味着您必须解决与跨AppDomain通信相关的编组和程序集解决问题。
答案 2 :(得分:-1)
您可能正在寻找的是依赖注入的概念。
依赖注入意味着模块X不是直接使用模块Y,而是依赖于接口,而应用程序告诉模块X哪个实现应该使用它,例如:使用模块Y。
有几种实现依赖注入的方法。一种是引用每个模块中的接口,并明确让应用程序使用正确的接口实现配置每个模块。
实现它的第二个问题(可能在你的情况下最有用)是使用中央注册表。定义您希望在应用程序中拥有的所有接口。这些是您要动态更改实现的接口。然后定义这些接口的标识。这些可以是字符串或整数或GUID。 然后在应用程序中创建一个映射,将映射映射到接口,并使用正确的接口实现填充映射。在C ++应用程序中(我还不熟悉C#),这可能是这样的:
std::map<std::string,IInterface> appInterfaces;
appInterfaces["database"] = new OracleDatabaseModule();
appInterfaces["userinterface"] = new VistaStyleUserInterface();
当他们想要使用其中一个模块时,让所有模块都进入这个中央注册表。确保它们不直接访问模块,但它们只通过注册表传递。 E.g。
MyModule::someMethod()
{
IDatabaseInterface *dbInterface = dynamic_cast<IDatabaseInterface *>(appInterfaces["database"]);
dbInterface->executeQuery(...);
}
如果您现在想要更改应用程序中接口的实现,您只需更改注册表中的条目,如下所示:
IInterface *iface = appInterfaces["database"];
if (iface) delete iface;
appInterface["database"] = new SqlServerDatabaseInterface();