【IT168 技术】面临的问题
在开发插件系统中,我们通常会面临这样的问题:
一些功能并不是在开启时就要被使用的,例如VS中的大量功能对一个大部分程序员来说用不着,但框架本身却应该向用户提供该插件的相应信息?
在可视化的插件功能列表中,我们不仅希望提供简单的插件名称信息,更希望能以图片,或动画等形式展示其功能特性,便于用户选择。
插入辅助类来解决上一个问题? 想法虽好,但破坏了“插件”的精髓,它应该是独立可插拔的,如果存在其之外的辅助类,那真是得不偿失。
据我所知,.NET的MEF插件系统提供了完整的插件系统框架,但可定制化程度不高。 一些插件功能是不需要每次都调用的,如果实例化所有的插件会带来很大的资源开销,而且不方便管理。因此本文将通过一些技巧,实现本文标题的目标:不实例化获取插件信息和可视化方法。我的项目本身是基于WPF的,但基本不影响整个文章的通用性。
如果想了解更多关于作者对本问题早期的思考,请看 我上一篇关于.NET插件系统的文章。
2. 改造attribute,提供类信息的方法
attribute 即可以应用于编程元素(如类型、字段、方法和属性 (Property))的描述性声明。属性与 .NET Framework 文件的元数据一起保存,并且可用于向公共语言运行时描述代码或影响应用程序的运行时行为。
通过继承atrribute,扩展我们想要获得的使用方法,下面的代码通过自定义attribute,提供其插件的名称,描述,资源定义和类型。
/// 自定义的Attribute,可在框架中提供程序集名称等信息
///
public class XFrmWorkAttribute : Attribute // 必需以System.Attribute类为基类
{
public string DetailInfo //提供该类的详细文字性说明
{
get;
set;
}
private string mainKind;
public string MainKind //该类类别
{
get { return mainKind; }
set { mainKind = value; }
}
private string name;
public string Name //提供类名称
{
get { return name; }
set { name = value; }
}
private string myresource;
public string myResource //提供资源名称
{
get { return myresource; }
set { myresource = value; }
}
// 值为null的string是危险的,所以必需在构造函数中赋值
public XFrmWorkAttribute(string thisName, string thisKind, string thisDetailType)
// 定位参数
{
this.MainKind = thisKind;
this.Name = thisName;
this.DetailInfo = thisDetailType;
}
// 值为null的string是危险的,所以必需在构造函数中赋值
public XFrmWorkAttribute(string thisName, string thisKind, string thisDetailType, string thisResource)
// 定位参数
{
this.MainKind = thisKind;
this.Name = thisName;
this.DetailInfo = thisDetailType;
this.myresource = thisResource;
}
}
自定义可由项目需求进行,具体请参考相关文档,此处并不打算具体说明。本定义中,MainKind字段用于存储该插件类型,这在一些搜索方法中是必要的。 而myResource字段可保存当前类的资源URI。这在WPF程序中尤为有效,在程序集中可嵌入图形,音乐甚至视频资源,可提供更好的用户体验。 而DetailInfo字段可保存对该插件的一些文字性描述。
下面展示该attribute的使用方法
2 public partial class Unity3DController : UserControl,IProgramWPF,IProgramUI
3 {
4 //类方法代码
5 }
类attribute使用实例
其四个构造函数的参数分别是:名称,类型(一般是实现的接口),类说明,资源名称。
3. 主程序框架动态查找可用插件的方法
主程序框架如何获知当前插件的定制信息,并在需要的时候实例化呢? 我们可以设计一个类来存储当前插件的信息,即下面的ObjectBasicInfo。 myLOGOUrl可理解为上述的资源路径,需要特别注意的是Type: 我们在此处存储了该类的Type,使得能在需要的时候实例化之。
那么,如何执行插件搜索呢? 网上已经有大量的说明和介绍。 不外乎是搜索某一程序集,获取所有类型Type,并查询其是否实现了某类接口。 但当工程中有不止一种插件类型时,我们就该考虑实现代码复用:
可以定义一个特殊的类SingletonProvider,考虑到该类功能较为固定,采用单例模式实现。 在内部,给出了一个静态字典和两个静态方法:
static Dictionary> myObjectBasicInfoDictionary: 插件字典:使用可通知集合ObservableCollection作为字典值,字典Key是接口名称(再次声明,此接口非一般意义上的接口,而是某种对插件分类的某种定义或约束,当然,可以使用C#的接口实现) 使用ObservableCollection仅仅是因为它在集合项目更改时可提供通知,方便WPF的数据绑定,如果不需要此项,你完全可以修改成你想要的集合类型如List
public static ObservableCollection
具体信息可以查看具体代码, 当字典中已经存在该接口插件集合,则直接返回结果,否则执行查询。需要注意:Assembly assembly = Assembly.GetCallingAssembly(); 可获得当前被调用的程序集,这就保证了查找不同接口时是在保存当前插件程序集的dll上动态执行的,这点非常重要,就可不用提供程序集名称。 另外,当找到某一满足需求的Type时,就可以查找当前type所有的attribute,并获取信息所有保存至字典中。 在下次调用同样接口的插件列表时,就可以不用再次执行查找。