[连载]《C#通信(串口和网络)框架的设计与落实》- 9.插件引擎安排

目       录

第⑩章           插件引擎设计… 2

9.1           框架的契约-接口… 2

9.2           插件的雏形-抽象类… 3

9.3           达成接口… 4

9.4           反射机制… 5

9.5           反射工具类… 8

9.6           小结… 9

 

C++,第拾章     插件引擎陈设

在介绍《第玖章
宿主程序详细安排》以前对接口和插件的连锁内容开始展览一下完好无损介绍,在安插宿主程序的时候会用到那个知识,也是宿主程序与插件之间相互的主旨内容。

9.1    框架的契约-接口

    
插件式框架的宿主程序运维后,它首先会加载相应的布局文件(例如:设备驱动配置文件等),找到呼应的插件程序集,那些程序集以DLL文件格式存在,框架的宿主程序会找到内定的插件类型,由插件引擎依照插件类型(例如:IRunDevice)生成对象实例,由框架的宿主程序的管理器对插件实例进行管制和调度。

   
贰个插件程序集可能包含八个插件类型,那么框架宿主程序是何等鉴定识别那几个连串是或不是为要加载的插件呢?每一种插件对象都有四个地方标识-接口,这几个标识在框架设计中被称呼“通讯契约”。接口能够被作为是一种概念了供给的方式、属性和事件的集合,由此宿主程序就能够经过那种契约来生成现实的实例对象,并对其余零件或接口公开可操作的靶子。

   
插件式框架当作贰个高聚合低耦合的阳台,它的机能定义与效益达成之间是分手的。只要符合插件规范的二遍开发组件都足以挂载到框架平马普托,而它并不并心那几个零件的具体效果。当然,框架平台提供了某些必备的消息、机制来担保这一个零件能够符合规律达成2回开发的职能。

   
在富有多少个逻辑层次的结构划设想计中,各层之间的通讯大多通过接口来兑现,接口不会随便改变,如若二个层的法力产生变化,不会影响此外层;只要健康完结了接口的机件效率,那么程序的运营就不曾难点。那种做法使得各层之间的相互影响下跌到低于,由此可见,接口在多事情层级中可见更好的解耦。

    在大多数效率性的编制程序和设计工作中,很少需求考虑“接口(interface)
”的图景,借使我们唯有满足通过控件的措施在IDE上编制程序和使用.NET
Framework中一般的类库,恐怕永远不会在程序中动用到接口,尽管在C#等面向对象语言的语法书中读者会成千成万次见到过那一个词,也只是成功平常的职能,并未通晓面向对象编制程序的核心境想。

    
接口是相似作为的定义和契约。如猫和狗等动物,只必要将惯常的、公共性的质量、动作等概念在接口里,例如:有眼睛、可以吃东西等。即使分裂动物之间存在相当大差别,不过接口并不考虑它们分其余特点或效益的距离,例如:什么颜色的眼眸、吃什么事物等。它只关怀这么些项目都不能够不兑现接口定义的保有作用,而完结了那几个接口就足以被视作是一种动物。

    由此,接口的多少个至关心重视要的效益是:

n  定义八个档次都亟待的共用艺术、属性。

n  作为一种不可实例化的项目存在。

持续接口完结定义的主意、属性等,实际上是落到实处了一种政策。

9.2    插件的雏形-抽象类

接口与抽象类分外相像,例如两者都不能够new三个实例对象,却都得以作

为一种契约和定义被运用。可是接口和抽象类有精神的例外,那些差异包罗:

n  接口没有其余达成部分,然则抽象类能够持续接口后有的贯彻代码。

n  接口没有字段,可是抽象类可以分包字段。

n  接口能够被协会(Struct)继承,不过抽象类不行。

n  抽象类有构造函数和析构函数。

n  接口仅能一而再自接口,而抽象类能够一连自其余类和接口。

n  接口帮忙多再三再四,抽象类仅协助单根继承。

在MSDN的相关内容中,给出了如下关于接口与抽象类的提议:


如若预测要创设组件的八个本子,则开创抽象类。抽象类提供简单易行的方式来控制组件版本。通过创新基类,全体继承类都随更改自动更新。另一方面,接口一旦创建就不能够改变,假如要翻新接口的本子,必须制造二个全新的接口。


如若创造的职能将在大范围的全异对象间使用,则使用接口。抽象类应首要用于关系密切的目的,而接口最适合为不相干的类提供通用的功力。


如若要规划小而精炼的功用模块,应该选拔接口。尽管要统一筹划大的效益单元,则应当利用抽象类。


要是要在组件的有着完结间提供通用的已完结效益,应该运用抽象类。抽象类允许一部分达成类,而接口不分包其余成员的落实。

9.3    完毕接口

接口和抽象类都得以当作“通讯契约”,为子类提供规范。上边定义二个接口和抽象类。

//定义一个接口
public interface IMyInterface
{
       void Action(int type);
       string Method(int para);
}

//定义一个抽象类
public abstract class BaseAbstract:IMyInterface

{
       public abstract void Action(int type); //继承此类抽象类时必须实现这个方法。

       public string Method(int para)         //实现这个方法
       {
              return para.ToString();
       }
}

此起彼伏接口的话,必要贯彻成套概念的主意或性质,如下代码:

public class MyClass1:IMyInterface
{
       public void Action(int type)
       {
              Console.WriteLine(type.ToString());
       }

       public string Method(int para)        
       {
              return para.ToString();
       }
}

接二连三抽象类的话,只供给贯彻抽象类没有落到实处的不二法门或性质,一般为架空方法或性能,如下代码:

public class MyClass2:BaseAbstract
{
       public void Action(int type)   //继承抽象类,只需要实现这个函数。
       {
              Console.WriteLine(type.ToString());
       }
}

9.4    反射机制

   
有了配备驱动或插件,还无法挂载到框架平台的宿主程序中。大家着想的题目是:已经有了随机三个类型插件程序集,框架平台怎样从程序集中依照类型定义在内部存款和储蓄器中生成插件对象?

  
回想普通情状下程序引用其余程序集组件的经过。首先,须要采用“添加引用”对话框加载程序集。然后,通过using关键字引用命名空间。最后,在命令空间下找到呼应的类,并new出来一个实例。那是一种静态加载程序集的艺术。

  
在插件式应用框架中,那种艺术并不吻合。宿主程序在编写翻译时并不知道它将要处理哪些程序集,更没有办法静态的将插件类型通过using关键字引入,那些都以在运营时才能获得的音讯。在这么的景象下,也无从选拔静态方法和new关键字来生成1个门类实例。而是要求在运营时取得有关消息动态加载程序集,这么些进度被叫作反射。

  
反射是动态发现类型音讯的一种能力,它好像前期绑定,援救开发职员在程序运行时行使程序集音信动态使用项目,这一个新闻在编写翻译时是未知的,反射还帮忙更尖端的行为,如能在运转时动态创立新品类,并调用那个项目标法子等。

   
JIT编写翻译器在将IL代码编写翻译花费地代码时,会查看IL代码中援引了那多少个类型。在运转时,JIT编写翻译器利用程序集的TypeRef和AssemblyRef元数据表的笔录项来显明哪二个主次集定义了引用的品种。在
AssemblyRef元数据记录项中记录了先后集强名称的一一部分—包含名称,版本,公钥标记和语言文化。那八个部分组成了1个字符串标识。JIT编写翻译器尝试将与这些标识匹配的先后集加载到如今的AppDomain中。假诺程序集是弱命名的,标识大校只包括名称。

   .NET
Framework中,为了达成动态加载,须求熟习Assembly、Type和Activator等工具类的点子。框架平台主要选用了Assembly工具类,这么些类中回顾Load、LoadFrom和LoadFile。

1.      Assembly的Load方法

  
在里面CL福特Explorer使用Assembly的Load方法来加载那么些程序集,这几个措施与Win32的LoadLibray等价。在其中,Load导致CL昂科拉对程序集应用二个版本重定向策略。并在GAC中搜寻程序集,若是没有找到,就去应用程序的基目录,私有路径目录和codebase钦点的位置查找。如若是三个弱命名程序集,Load不会向程序集应用重定向策略,也不会去GAC中检索程序集。即便找到将回到四个Assembly的引用,如果没有找到则抛出FileNotFoundException非凡。注意:Load方法假若已经加载一个平等标识的先后集只会不难的回来那些顺序集的引用,而不会去创制一个新的次序集。

大部分动态可扩张应用程序中,Assembly的Load方法是先后集加载到AppDomain的首要选择办法。那种格局索要钦赐程序集的标识字符串。对于弱命名程序集只用钦赐二个名字。

2.Assembly的LoadFrom方法

   
当大家精晓程序集的途径的场馆,能够使用LoadFrom方法,它同意传入二个Path字符串,在其间,LoadFrom首先调用AssemblyName的静态方法GetAssemblyName。这几个点子打开内定的文件,通过AssemblyRef元数据表提取程序集的标识,然后关门文件。随后,LoadFrom在个中调用Assembly的Load方法寻找程序集。到那里,他的表现和Load方法是千篇一律的。唯一分化的是,假如按Load的章程没有找到程序集,LoadFrom会加载Path路径钦赐的次序集。其它,Path能够是ULX570L。

3.Assembly的LoadFile方法

   
那么些点子初中一年级看和LoadFrom方法很像。但LoadFile方法不会在在那之中调用Assembly的Load方法。它只会加载钦定Path的程序集,并且那个方式能够从随机路径加载程序集,同一程序集固然在不一致的路径下,它同意被频仍加载,等于四个同名的主次集加载到了AppDomain中,那点和方面包车型客车两个主意完全不一致等。但是,LoadFile并不会加载程序集的注重项,也正是不会加载程序集引用的其它程序集,那会导致运维时找不到其他参照DLL的万分。要消除那些标题,供给向AppDomain的AssemblyResolve事件登记,在回调方法中显示加载引用的顺序集。类似于如此:

AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
       if (args.Name != null)
       {
              return Assembly.LoadFrom(string.Format("{0}\\plugin\\{1}.dll", Application.StartupPath, new AssemblyName(args.Name).Name));
       }
       return null;
}

     
越发注意:要测试LoadFile有没有加载引用的DLL,切不可将DLL拷贝到应用程序的根目录下测试,因为该目录是CL陆风X8加载程序集的默许目录,在那么些目录中一经存在引用的DLL,它会被加载,造成LoadFile会加载引用DLL的假象。能够在根目录下新建三个子目录如plugin,把引用的dll拷贝到这么些中进行测试。

    
反射机制也有它的缺陷:安全性和性质方面。不过,框架平台在起步的时候、以及扩张新装置驱动(插件)的时候须要采纳反射,一旦加载到宿主程序中,与静态引用程序集并未本质分歧,都以寄存在内部存款和储蓄器中。

9.5    反射工具类

插件式框架平台运用反射挂载设备驱动,在宿主程序中运维,须要一个专用的工具类来成功有关职能。代码定义如下:

/// <summary>
/// 一个轻便的 IObjectBuilder 实现
/// </summary>
public class TypeCreator : IObjectBuilder
{
       public T BuildUp<T>() where T : new()
       {
              return Activator.CreateInstance<T>();
       }

       public T BuildUp<T>(string typeName)
       {
              return (T)Activator.CreateInstance(Type.GetType(typeName));
       }

       public T BuildUp<T>(object[] args)
       {
              object result = Activator.CreateInstance(typeof(T),args);
              return (T)result;
       }

       /// <summary>
       /// 框架平台主要使用了这个函数。
       /// </summary>
       /// <typeparam name="T"></typeparam>
       /// <param name="assemblyname"></param>
       /// <param name="instancename"></param>
       /// <returns></returns>
       public T BuildUp<T>(string assemblyname, string instancename)
       {
              if (!System.IO.File.Exists(assemblyname))
              {
                     throw new FileNotFoundException(assemblyname + " 不存在");
              }
              System.Reflection.Assembly assmble = System.Reflection.Assembly.LoadFrom (assemblyname);
              object tmpobj = assmble.CreateInstance(instancename);
              return (T)tmpobj;
       }

       public T BuildUp<T>(string typeName, object[] args)
       {
              object result = Activator.CreateInstance(Type.GetType(typeName), args);
              return (T)result;
       }
}

9.6    小结

   
下一章节介绍宿主程序详细布署,须求对反射机制有早晚的打听,并且会使用到下面的工具类,并在此基础上进展扩张。

    框架平台就要全盘了,只须求一小步了。

 

小编:唯笑志在

Email:504547114@qq.com

QQ:504547114

.NET开发技术订盟:54256083

文书档案下载:http://pan.baidu.com/s/1pJ7lZWf

合法国网球国际赛址:http://www.bmpj.net