VS开发:.NET插件系统实现快速可视化

简介:

面临的问题

  在开发插件系统中,我们通常会面临这样的问题:

  一些功能并不是在开启时就要被使用的,例如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的使用方法

1   [XFrmWorkAttribute("Unity3D控制器", "IProgramWPF", "针对Unity3D的游戏编程接口", "/XFrmWork.XMove.Program;component/Images/Unity3D控制器.jpg")]
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 GetInstance(string interfaceName) : 查询被某种接口约束的所有插件方法:

  具体信息可以查看具体代码, 当字典中已经存在该接口插件集合,则直接返回结果,否则执行查询。需要注意:Assembly assembly = Assembly.GetCallingAssembly(); 可获得当前被调用的程序集,这就保证了查找不同接口时是在保存当前插件程序集的dll上动态执行的,这点非常重要,就可不用提供程序集名称。 另外,当找到某一满足需求的Type时,就可以查找当前type所有的attribute,并获取信息所有保存至字典中。 在下次调用同样接口的插件列表时,就可以不用再次执行查找。

 

 

 public static Object GetObjectInstance(string interfaceName, int index) 封装了的反射实例化方法。 类型参数是接口名称和在插件列表中的位置,为了简化, 类的构造函数必须是没有形参的。

  下面的代码做了详细的说明:

1 /// <summary>
2 /// 该类定义了插件系统通过attribute记录的类基本信息
3 /// </summary>
4 public class ObjectBasicInfo
5 {
6 public string myLogoURL { get; set; }
7 public string myName { get; set; }
8 public Type myType { get; set; }
9 public string myDetail { get; set; }
10 }
11 /// <summary>
12 /// 单例模式提供的插件搜索器
13 /// </summary>
14 public class SingletonProvider
15 {
16 SingletonProvider()
17 {
18 }
19 static Dictionary<string, ObservableCollection<ObjectBasicInfo>> myObjectBasicInfoDictionary = new Dictionary<string, ObservableCollection<ObjectBasicInfo>>();
20 
21 public static Object GetObjectInstance(string interfaceName, int index)
22 {
23 return Activator.CreateInstance(GetInstance(interfaceName)[index].myType); 
24 }
25 public static ObservableCollection<ObjectBasicInfo> GetInstance(string interfaceName)
26 {
27 if (myObjectBasicInfoDictionary.ContainsKey(interfaceName)) //如果字典中存在该方法,则直接返回结果
28 return myObjectBasicInfoDictionary[interfaceName];
29 else
30 {
31 
32 ObservableCollection<ObjectBasicInfo> tc = new ObservableCollection<ObjectBasicInfo>(); //执行查找方法
33 
34 Assembly assembly = Assembly.GetCallingAssembly();
35 Type[] types = assembly.GetTypes();
36 foreach (Type type in types)
37 {
38 if (type.GetInterface(interfaceName) != null && !type.IsAbstract)
39 {
40 
41 
42 // Iterate through all the Attributes for each method.
43 foreach (Attribute attr in
44 type.GetCustomAttributes(typeof(XFrmWorkAttribute), false))
45 {
46 XFrmWorkAttribute attr2 = attr as XFrmWorkAttribute;
47 tc.Add(new ObjectBasicInfo() { myLogoURL = attr2.myResource, myType = type, myDetail = attr2.DetailInfo, myName = attr2.Name });
48 }
49 }
50 }
51 myObjectBasicInfoDictionary.Add(interfaceName, tc);
52 return tc;
53 }
54 }
55 
56 
57 }
 

  插件搜索器

  使用时非常方便,下面我们会介绍如何使用他。

  4. 使用方法

  下面我们以一个具体场景介绍这一方法的使用: 假设有一个通信系统,要求动态增减通信功能,如USB,串口,WIFI,蓝牙等。 我们将其公共方法为接口ICommMethod ,所有通信方法都必须实现这一接口,并增加自定义的XFrmworkAttribute.

1 [XFrmWorkAttribute("标准蓝牙通信", "ICommMethod", "提供支持绝大多数蓝牙设备的socket蓝牙通信","/XFrmWork.XMove.Comm;component/Images/标准蓝牙.jpg")]
2 public class BluetoothAdvanced : AbstartComm
3 {
4 public BluetoothAdvanced():base
5 ()
6 {

8 }
9 }
其他方法不一一列举。

  我们在一个插件管理类中直接这样调用:

ComMethodList.DataContext = SingletonProvider.GetInstance("ICommMethod");
ComMethodList是我的系统中一个可用通信方法列表的WPF的Listbox控件。 直接将 ObservableCollection> 集合绑定到该控件的数据上下文中,即可显示当前所有的通信列表。 Listbox中的对应index即插件集合的index,通过上面描述的实例化方法,就能反射实例化之。 讨论WPF的数据绑定超过了本文的范畴,但可查询相关资料。读者可以自行设计和使用该集合提供的数据。显示效果如下图:

 

  5. 在已实例化的对象中获取该类的attribute数据

  还有一个遗留问题,即在实例化的对象中,我们依旧要获得类的名称,描述和其他相关信息,如何做呢? 我们定义如下的attribute方法实现之

public class AttributeHelper
2 {
3 public static XFrmWorkAttribute GetCustomAttribute(Type source) 
4 {

6 object[] attributes = source.GetCustomAttributes(typeof(XFrmWorkAttribute), false);
7 foreach (object attribute in attributes)
8 {
9 if (attribute is XFrmWorkAttribute)
10 return (XFrmWorkAttribute)attribute;
11 }
12 
13 return new XFrmWorkAttribute("不存在定义","NULL","NULL","无定义资源");
14 }
15 }
若该类未实现自定义的attribute,返回一个“空”值,提醒设计者或开发人员。

  使用起来也很简单,例如我们想获得该对象的“名称”:

 public string PublicName

  {

  get { return AttributeHelper.GetCustomAttribute(this.GetType()).Name; }

  }

  注意,属性访问器中,显然只应该实现get方法,它是运行时的不可修改对象。

  6. 其他

  本文的来源是作者项目中的实际需要,不实例化类而获得类的相关信息是一种很普遍的需求,attribute是一种做法,也可以通过xml实现,很多插件系统就是这么做的,但对于轻量级的系统来说,attribute可能更合适,而单例模式的引用给该方法带来了很大的方便。 有任何问题欢迎随时留言交流。
 










本文转自 wws5201985 51CTO博客,原文链接:http://blog.51cto.com/wws5201985/778509,如需转载请自行联系原作者
目录
相关文章
|
14天前
|
人工智能 量子技术 C#
【专栏】.NET 开发:开启数字化新时代
【4月更文挑战第29天】.NET开发在数字化新时代中发挥关键作用,借助跨平台能力、高性能和现代编程语言支持,如C#,助力企业实现数字化转型。通过企业级应用开发、移动应用和云计算集成,.NET加速业务流程和提升用户体验。未来,.NET将涉足AI、ML、MR/AR及量子计算,持续推动技术创新和数字化转型。开发者应提升技能,适应高性能需求,把握发展机遇。
|
14天前
|
缓存 监控 算法
【专栏】.NET 开发:实现卓越性能的途径
【4月更文挑战第29天】本文探讨了.NET开发中的性能优化,强调了理解性能问题根源和使用分析工具的重要性。基础优化包括代码优化(如减少计算、避免内存泄漏)、资源管理及选择合适算法。高级策略涉及并行编程、缓存策略、预编译(AOT)和微服务架构。持续性能测试与监控是关键,包括性能测试、监控分析和建立优化反馈循环。开发者应持续学习和实践性能优化,以构建高性能应用。
|
14天前
|
开发框架 .NET C#
【专栏】理解.NET 技术,提升开发水平
【4月更文挑战第29天】本文介绍了.NET技术的核心概念和应用,包括其跨平台能力、性能优化、现代编程语言支持及Web开发等特性。文章强调了深入学习.NET技术、关注社区动态、实践经验及学习现代编程理念对提升开发水平的重要性。通过这些,开发者能更好地利用.NET构建高效、可维护的多平台应用。
|
14天前
|
机器学习/深度学习 vr&ar 开发者
【专栏】.NET 技术:引领开发新方向
【4月更文挑战第29天】本文探讨了.NET技术如何引领软件开发新方向,主要体现在三方面:1) 作为跨平台开发的先锋,.NET Core支持多操作系统和移动设备,借助.NET MAUI创建统一UI,适应物联网需求;2) 提升性能和开发者生产力,采用先进技术和优化策略,同时更新C#语言特性,提高代码效率和可维护性;3) 支持现代化应用架构,包括微服务、容器化,集成Kubernetes和ASP.NET Core,保障安全性。此外,.NET还不断探索AI、ML和AR/VR技术,为软件开发带来更多创新可能。
|
14天前
|
物联网 vr&ar 开发者
【专栏】.NET 技术:为开发注入活力
【4月更文挑战第29天】本文探讨了.NET技术的创新,主要体现在三个方面:1) .NET Core实现跨平台开发革命,支持多种操作系统和硬件,如.NET MAUI用于多平台UI;2) 性能提升与生产力飞跃,C#新特性简化编程,JIT和AOT优化提升性能,Roslyn提供代码分析工具;3) 引领现代化应用架构,支持微服务、容器化,内置安全机制。未来,.NET 7将带来更多新特性和前沿技术整合,如量子计算、AI,持续推动软件开发创新。开发者掌握.NET技术将赢得竞争优势。
|
Java C# 开发工具
.Net码农学Android---系统架构和基本概念
原文:.Net码农学Android---系统架构和基本概念 至此,你应该已经完成以下前期准备事情: 1.安装完JDK 2.安装完SDK(并在Manager中进行相关版本的更新) 3.相关IDE(如eclipse) 4.安装完ADT 5.安装完AVD(如果你是真机模拟的话也可以不安装) 前期环境搭建基本完成,并按照网上的教程可以运行出HelloWorld,确保可以流程走的通。
956 0
|
4月前
|
开发框架 前端开发 .NET
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
47 0
|
18天前
|
开发框架 前端开发 JavaScript
JavaScript云LIS系统源码ASP.NET CORE 3.1 MVC + SQLserver + Redis医院实验室信息系统源码 医院云LIS系统源码
实验室信息系统(Laboratory Information System,缩写LIS)是一类用来处理实验室过程信息的软件,云LIS系统围绕临床,云LIS系统将与云HIS系统建立起高度的业务整合,以体现“以病人为中心”的设计理念,优化就诊流程,方便患者就医。
22 0
|
2月前
|
开发框架 前端开发 .NET
进入ASP .net mvc的世界
进入ASP .net mvc的世界
32 0
|
2月前
mvc.net分页查询案例——mvc-paper.css
mvc.net分页查询案例——mvc-paper.css
5 0