[连载]《C#通讯(串口和网络)框架的宏图与贯彻》- 9.插件引擎计划

目       录

第天问           插件引擎设计… 2

9.1           框架的契约-接口… 2

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

9.3           实现接口… 4

9.4           反射机制… 5

9.5           反射工具类… 8

9.6           小结… 9

 

第离骚     插件引擎计划

在介绍《第10章
宿主程序详细规划》从前对接口和插件的相干内容开展一下完好介绍,在规划宿主程序的时候会用到这些文化,也是宿主程序与插件之间交互的焦点内容。

9.1    框架的契约-接口

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

   
一个插件程序集可能包括六个插件类型,那么框架宿主程序是如何识别那么些序列是否为要加载的插件呢?每个插件对象都有一个地位标识-接口,那些标识在框架设计中被叫做“通讯契约”。接口可以被作为是一种概念了不可或缺的主意、属性和事件的聚集,因而宿主程序就可以经过这种契约来生成现实的实例对象,并对其它零件或接口公开可操作的对象。

   
插件式框架当作一个高聚合低耦合的平台,它的功用定义与效果实现之间是分开的。只要符合插件规范的二次开发组件都足以挂载到框架平博洛尼亚,而它并不并心这么些零件的切切实实效果。当然,框架平台提供了一部分少不了的信息、机制来确保这个零件可以健康实现二次开发的效益。

   
在有着几个逻辑层次的结构设计中,各层之间的通信大多经过接口来贯彻,接口不会随随便便改变,倘使一个层的效能发生变化,不会潜移默化另外层;只要正常实现了接口的零部件效能,那么程序的周转就从未问题。这种做法使得各层之间的相互影响降低到最低,综上可得,接口在多事情层级中可以更好的解耦。

    在大部效用性的编程和计划性工作中,很少需要考虑“接口(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关键字来生成一个连串实例。而是需要在运行时拿到有关信息动态加载程序集,这些过程被叫做反射。

  
反射是动态发现类型音信的一种力量,它仿佛中期绑定,帮衬开发人士在程序运行时选择程序集消息动态使用项目,这一个音讯在编译时是雾里看花的,反射还帮助更高级的行事,如能在运转时动态创造新品类,并调用这个项目标不二法门等。

   
JIT编译器在将IL代码编译成本地代码时,会查看IL代码中引用了这些类型。在运行时,JIT编译器利用程序集的TypeRef和AssemblyRef元数据表的记录项来确定哪一个主次集定义了引用的项目。在
AssemblyRef元数据记录项中著录了先后集强名称的各个部分—包括名称,版本,公钥标记和语言文化。这六个部分组成了一个字符串标识。JIT编译
器尝试将与这一个标识匹配的顺序集加载到当下的AppDomain中。假诺程序集是弱命名的,标识中将只包含名称。

   .NET
Framework中,为了贯彻动态加载,需要熟练Assembly、Type和Activator等工具类的不二法门。框架平台紧要运用了Assembly工具类,那多少个类中包括Load、LoadFrom和LoadFile。

1.      Assembly的Load方法

  
在其中CLR使用Assembly的Load方法来加载这个程序集,这一个点子与Win32的LoadLibray等价。在中间,Load导致CLR对程序集应用一个本子重定向策略。并在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可以是URL。

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拷贝到应用程序的根目录下测试,因为该目录是CLR加载程序集的默认目录,在这几个目录中一经存在引用的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

相关文章