保持活动对象的插件系统

马斯特·阿尔多

我有一个需要在运行时加载和卸载一些.dll文件形式的插件的应用程序。这些dll文件包含一个或多个从我的抽象类Module派生的类,如下所示:

public abstract class Module : MarshalByRefObject
{
    //various fields here, not reported as they are not useful to understand

   public abstract void Print();

}

据我了解:在C#中,卸载程序集及其类的唯一方法是将程序集放入专用的Appdomain中,然后在不再需要时将其卸载,并且AppDomain之间进行通信的唯一方法是从MarshalByRefObject或MarshalByValueObject派生。然后,当我将模块的引用传递给另一个AppDomain时,我实际上并没有获取对象,而是获得了透明代理。任何人都可以确认我上面写的内容吗?

好的,现在是真正的问题:假设在我的文件PersonPlugin.dll中,我有一个扩展了Module的“ Person”类。此类包含字段名称,姓氏和电话号码。

定期调用方法Print(定期调用print没有意义,但这只是示例)将打印这3个字段。

后来,我使用新版本的Person类构建了一个新的dll,该类具有一个名为address的新字段,并且Print方法现在还会打印该地址。注意:插件是由第三方制作的,我不知道新版本是与旧版本相似还是完全不同,但是就我而言,可以肯定的是,类Person在各个版本之间不会有太大差异。

然后,我用这个新文件替换旧的dll文件(该dll在专用的appdomain中被影影复制,因此该文件是可写的/可删除的)

FileSystemWatcher注意到更改,并且:

  1. 为新的dll创建一个新的appdomain

  2. 保存旧的实例化Person对象的状态

  3. 将旧的Person类中的Person对象分配给新的Person类

  4. 卸载包含旧Person类的appdomain

  5. 应用程序开始打印还包含地址的新字符串(至少在开头为null)

第2点和第3点是我的问题的核心:如何在卸载它们的类后如何使这些对象(或至少它们的字段)保持活动,然后实例化一个具有相同名称和相似属性的新类,但实际上这是另一个班级?

这是我到目前为止的内容:

  1. 使用反射,我可以在字典中复制每个Person对象的每个字段,删除旧的对象,然后实例化新的对象,并在可能的情况下复制回这些字段(如果不存在旧的字段,则跳过该字段,如果存在一个新的字段,则得到该字段)其默认值)

  2. 使用类似MessagePack的东西自动序列化旧的Person对象,然后反序列化为新的Person对象

但是,我没有进行任何测试以查看这两种解决方案是否可行,主要是因为我想在开始编写代码之前先知道这两种解决方案是否可能在现实中发挥作用或者只是在我的脑海中,并且因为也许你们中有些人更完善的/可行的解决方案,甚至更好的已经可以做到的框架/库。

更新:

好的,我意识到在不带上下文的情况下问这个问题可能会引起一些误解,所以:我要问的问题是关于我的论文的,这是一个模块化的最小游戏引擎(不是服务器)。这个想法是使引擎模块化,并使其易于测试各种功能,可以在运行时立即更改其模块,并立即应用更改,而无需重新启动或失去游戏中“ actor”的状态。一个dll包含1到许多模块。每个dll都加载到1个AppDomain中,因此,如果我卸载该appdomain,则会卸载其中的每个模块。同一appdomain中的模块可以相互直接引用,因为在卸载时,它们会一起卸载。不同程序集中的模块(也就是不同的AppDomain)中的模块使用消息总线进行通信,并且永远不会有直接引用。

重要的是模块代表的内容取决于引擎的用户:模块可以代表单个游戏对象(例如汽车)或整个物理模块。我将不进一步研究细节,因为它们现在没有用,但是我想要实现的目标可以通过以下示例来说明:

我有一个dll,其中包含一个名为Car的模块,该模块始终向前移动。我用包含模块Car的新dll更改了dll,该模块现在始终向后移动。结果是,一旦我替换了dll,汽车将立即反转方向。

当然,这个例子很愚蠢,有很多方法可以更轻松地实现这一目标。但是,这也适用于在代码中发现错误,更正,提交并错误消失,甚至更复杂的情况下。

这就是为什么我需要保持活动对象(我的意思是,保持它们的状态)以及整个系统。

关于您的观点2:对于为什么我不允许共存2个相同类型但不同版本的模块没有严格的限制,只要它们在不同的名称空间或不同的dll中就可以了,因为在我的系统中每个模块都被标识和引用具有不同的ID

兰迪

首先,让我评论您的第一点。

据我了解:在C#中,卸载程序集及其类的唯一方法是将程序集放入专用的Appdomain中,然后在不再需要时将其卸载,并且AppDomain之间进行通信的唯一方法是从MarshalByRefObject或MarshalByValueObject派生。然后,当我将模块的引用传递给另一个AppDomain时,我实际上并没有获取对象,而是获得了透明代理。任何人都可以确认我上面写的内容吗?

确认的。

这是完全准确的。.NET程序集AppDomain在加载后无法从中卸载整个AppDomain必须卸载。

如果是模块化系统,则必须AppDomain在主机应用程序和插件之间以单独的编组数据加载插件。

现在,解决您有关动态加载模块并保持其状态的问题。您绝对可以这样做,但是您需要在模块和主机应用程序之间建立合同。

无需标记您的模块,MarshalByRefObject因为这将对模块实现带来额外的限制,并带来额外的开销。取而代之的是,我将使用接口来表示合同,从而使我的模块合同尽可能轻便。

public interface IModule
{
    Person GetPerson();
    void Print();
}

任何需要在主机应用程序和模块之间传递的数据都需要继承,MarshalByRefObject因此,如果您打算来回传递它,则Person类应该看起来像这样。

public class Person : MarshalByRefObject
{
    ...
}

现在,关于持久状态,Person即使已添加新属性。

由于您的主机应用程序不需要了解这些新属性,因此应将这些类型的对象的持久性移动到位于插件ModuleManager内部的AppDomain,并在您的方法上公开一些IModule知道如何执行实际持久性的方法。

public interface IModule
{
    Person GetPerson();
    void Print();

    IDictionary<string, object> GetState();
    void SetState(IDictionary<string, object> state);
}

public class ModuleManager : MarshalByRefObject
{
    public IModule Module { get; set; }

    public void Initialize(string path)
    {
        // Load the module
        // Setup file watcher
    }

    public void SaveState(string file)
    {
        var state = this.Module.GetState();
        // Write this state to a file
    }

    public void LoadState(string file)
    {
        var state = this.ReadState(file);
        this.Module.SetState(state);
    }

    private IDictionary<string, object> ReadState(string file)
    {
        ...
    }
}

ModuleManager应负责保存模块的状态,所以每个模块开发人员不必实现此功能。它要做的就是在键/值对的字典中读写状态。

您会发现此方法易于管理,而不是尝试在宿主应用程序中执行所有持久性。

这里的想法是在主机应用程序和插件之间封送尽可能少的数据。每个插件都知道哪些字段可用,Person因此让他们负责指示需要保留的字段

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

保持活动对象的插件系统

来自分类Dev

通过lambda使对象保持活动状态

来自分类Dev

从领域删除模型,但使对象保持活动状态

来自分类Dev

pybind11使对象保持活动状态

来自分类Dev

通过lambda使对象保持活动状态

来自分类Dev

事件是否会使对象保持活动状态?

来自分类Dev

Web API / WCF如何使对象保持活动状态

来自分类Dev

强烈引用“自我”以使对象保持活动(临时):邪恶?

来自分类Dev

方法参考使对象在Swift中保持活动状态

来自分类Dev

在调用单个网页之间使对象保持活动状态

来自分类Dev

如何在具有scrollspy的导航系统中的CSS中保持元素处于活动状态?

来自分类Dev

系统服务由于代码=退出,状态= 1 /失败而保持不活动状态

来自分类Dev

活动WordPress插件

来自分类Dev

检查活动的Grails插件

来自分类Dev

FutureTask保持活动状态

来自分类Dev

HTTP保持活动超时

来自分类Dev

与Superagent保持活动连接

来自分类Dev

与Superagent保持活动连接

来自分类Dev

如何使PsychoPy对象保持可见。Window是Windows 7上最顶层(活动的)窗口?

来自分类Dev

如果某个其他对象未引用Singleton,它如何或为什么保持活动状态?

来自分类Dev

CFRetain是否可以使桥接的ObjC对象保持活动状态?

来自分类Dev

仅在对象实例的生存期内使Python解释器保持活动状态

来自分类Dev

如何在关闭应用程序的情况下使对象保持活动状态

来自分类Dev

系统如何保持安全?

来自分类Dev

PhoneGap Android插件-关闭插件活动

来自分类Dev

Phonegap插件活动导入布局

来自分类Dev

Phonegap插件活动导入布局

来自分类Dev

活动停止后保持服务活动

来自分类Dev

Node.JS插件系统

Related 相关文章

热门标签

归档