- 浏览: 647607 次
- 性别:
- 来自: 石家庄
文章分类
最新评论
-
abao1:
老贾 在安装IDEA的过程中,在激活步骤时,按如下操作即可: ...
IntelliJ IDEA 2016注册方法和注册码 -
bo_hai:
./usr/bin/java: symbol lookup ...
jmagick安装步骤 -
wxcking:
不错的, 收藏一下
JAVA使用POI生成Excel文件 -
zgyfh:
大哥,密码是多少啊?zgyfh@tom.com谢谢了!新手学习 ...
WPF做的必备示例 -
记忆无泪:
jiasongmao 写道你的邮箱是多少,我可以发源代码到邮箱 ...
WPF做的必备示例
注意:文本为外部引用 1 引言 在信息系统开发,用户业务功能变化预先不可知,故要提高系统后期的业务扩展。一般情况下用户需求发生变化,要重新编写代码,编译,生产部署包,然后再更新用户程序。这样的过程比较繁琐。 本文讨论生成后的应用系统与外部编译的业务库实现动态绑定,应用程序在运行过程中动态绑定要实现的外部业务,当业务发生变化,也只是替换这些外部的动态库,不用重新对应用程序进行修改和编译,实现了耦合绑定。同时,业务实例对象可以在程序运行时实现实例化,达到了封装效果。并且降低了调用代码和具体实现类代码的耦合,增强灵活性和可复用性,增加了软件的可维护性。 C#提供的反射机制,再结合自适应数据参数的传递,通过这个技术,我们可以将应用框架中的扩展点以插件式程序集的方式来动态加载、构建,从而实现可动态扩展的应用程序。 2 反射机制的基础知识 反射[1][2]是.NET中重要机制,通过反射,可以在运行时获得.NET中每一个类型(包括类、结构、委托、接口和枚举等)的成员,包括方法、属性、事件,以及构造函数等。还可以获得每个成员的名称、限定符和参数等。.NET的应用程序结构分为应用程序域、程序集、模块、类型和成员几个层次,公共语言运行库加载器管理应用程序域,这种管理包括将每个程序集加载到相应的应用程序域以及控制每个程序集中类型层次结构的内存布局。程序集包含模块,而模块包含类型,类型又包含成员,反射则提供了封装程序集、模块和类型的对象。我们可以使用反射动态地创建类型的实例,将类型绑定到现有对象或从现有对象中获取类型,然后调用类型的方法或访问其字段和属性。 反射通常具有以下用途:① 使用Assembly定义和加载程序集,加载在程序集清单中列出的模块,以及从此程序集中查找类型并创建该类型的实例;② 使用Module了解如下的类似信息,如模块的程序集以及模块中的类等:③ 使用CoustructorInfo了解如下的类似信息,如构造函数的名称、参数、访问修饰符(如public或private)和实现详细信息(如abstract或virtua1)等;④ 使用MethodInfo来了解如下的类似信息,如方法的名称、返回类型、参数、访问修饰符(如public或private)和实现详细信息(如abstract或virtua1)等;⑤ 使用FieldInfo来了解如下的类似信息,如字段的名称、访问修饰符(如public或private)和实现详细信息(如static)等,并获取或设置字段值;⑥ 使用EventInfo来了解如下的类似信息,如事件的名称、事件处理程序数据类型、自定义属性、声明类型和反射类型等,并添加或移除事件处理程序:⑦ 使用PropertyInfo来了解如下的类似信息,如属性的名称、数据类型、声明类型、反射类型和只读或可写状态等,并获取或设置属性值;⑧ 使用ParameterInfo来了解如下的类似信息,如参数的名称、数据类型、参数是输入参数还是输出参数,以及参数在方法签名中的位置等。 3.总体设计思路 插件是一种遵循一定规范的应用程序接口编写出来的程序模块。当应用程序已经部署,但业务发生了变化,这样可以通过读取插件配置信息,载入新的应用构件,实现变化的业务。 对于应用系统的框架而言,扩展点是框架中预先定义的一些“点”。 在框架复用中应用构件的组装需要基于扩展点进行。构造性和演化性是软件的两个本质特征,作为一类重要的可复用软件制品。而基于扩展点可以组装不同的应用构件以适应领域的变化性。则体现了框架对于软件演化特征的支持[3]。 本文涉及到几个概念,插件配置定义,接口定义,方法定义和调用参数定义和返回参数定义。在本插件平台中,配置文件描述插件配置定义,接口定义,方法定义。对于调用参数定义和返回参数定义则采用通用对象和动态对象组[4]来实现传入和返回参数。 插件平台的实现过程如图1所示。当平台运行初始化时,通过读取XML配置信息,装载DLL,通过C#的反射机制分析DLL里的全部实现类和方法。外部构件可以在平台容器中被实例化,并执行插件点的方法。实现的算法不再是编码硬绑定。 图1 PlugPlatform整个过程图 这样,应用程序在运行过程中动态绑定要实现的外部业务,当业务发生变化,也只是替换这些外部的动态库,不用重新对应用程序进行修改和编译,实现了耦合绑定。 4.具体实现 PlugPlatform平台包括四个部分:① 配置文件的获取和解析;② 通用参数和动态参数组处理;③ 插件平台装载DLL并执行外部方法;④ 异常处理。 4.1 配置文件的获取和解析 配置文件以XML Schema为基础,分为两种类型,一种是类配置文件,主要描述关于外部DLL中的类以及方法的内容。第二种配置文件是接口配置文件,主要描述关于外部DLL中的接口以及方法的内容。 类配置文件的XSD如图2所示。 图2 类配置文件的schema图 按照此XSD形成的配置XML如图3所示。 图3 类配置文件的XML树 同理可以接口配置文件的XSD内容(如图4)和XML树(如图5)。 图4 接口配置文件的schema图 图5 接口配置文件的XML树 在实现将XML树状结构的数据转换为二维MethodObject哈希表,MethodObject哈希表是一key/value的键值对,其中key通常可用来快速查找,value用于存储对应于key的值。MethodObject类数据结构如下: MethodObject哈希表中key为保证内容的唯一性而采用方法的全名。可以,这样形成的主键可以进行快速查找。value用来存储MethodObject对象。同时MethodObject对象与ClassObject对象,InterfaceObject对象和DLLFile对象都是多对一的关系,所以,一旦获得了MethodObject对象,就可以反推出ClassObject对象,InterfaceObject对象和DLLFile对象。 图6 配置XML转MethodObject哈希表 根据XML Schema可以构建XML文档树,对XML文档树的节点进行分层遍历,然后采用递归算法,依次把XML文档树上最边上的叶子转化为方法对象哈希表,实现方式如图6所示。 4.2通用参数和动态参数组处理 对于外部的方法,要传入参数,同时也获得结果。这些都要用一些通用的数据结构来描述。参数必须可以支持任何类型,是一个通用性的参数。通过创建一个数据的通用类,可以保证支持任何数据类型。 由于传入和传出的参数有多有少,这就要求参数组能实现随意的自动增长和减少。通过设计一个动态自增长的参数数组就可以实现。设计模型如图7表示。 图7 DataValueObject类和DynamicArrayObject类的设计模型 动态参数组的增加数组对象方法如下: 动态参数组的删除数组对象方法如下: 4.3插件平台动态装载DLL并执行 PlugFramework是整个PlugPlatform平台的核心内容。主要实现检查和装载DLL文件,动态创建实例化对象,验证并执行外部方法。 图8 PlugPlatform动态装载的全过程 PlugPlatform依据配置文件可以了解装载的外部DLL文件和要求执行的类或接口方法。整个装载和的调用过程如图8说明。 PlugFramework包含有类和接口,这些类和接口之间有继承、实现、关联关系。其类图如图9所示。 图9 PlugPlatform类图 PlugFramework各个类的具体详细描述如下: 序号 名称 实现功能 备注 1 Plus.PlusConfig 插件平台的配置信息类,可创建工厂类 类 2 Plus.PlugFactory 插件平台的工厂类,可以创建方法实现类 类 3 Plus.IAction 方法实现的接口 接口 4 Plus.Framework.PlugAction 方法实现的抽象祖先类 类 5 Plus.Framework.ClassAction 类对象的实现类 类 6 Plus.Framework.InterfaceAction 接口对象的实现类 类 7 Plus.Framework.AssemblyManager Assembly的管理类,生成Assembly。 类 8 Plus.Framework.TypeManager Type的管理类,可实现对Type、Object的生成和检查。包括动态方法的调用。 类 动态调用外部方法的核心代码如下所示: 图10表示PlugPlatform实现全过程。下面分别对每个步骤做一个详细描述: ① 外部应用请求动态调用。 ② PlusConfig类根据配置文件创建PlugFactory对象。 ③ PlugFactory对象创建一个Action对象。 ④ Action对象获得MethodObject对象组,逆向产生DllFileObject对象。 ⑤ 根据DllFileObject对象中的DLL文件信息,Action对象通过AssemblyManager类获得Assembly对象。 ⑥ Action对象使用Assembly对象创建TypeManager对象。 ⑦ Action对象传递MethodObject对象给TypeManager对象。 ⑧ TypeManager对象可依据MethodObject对象获得ClassObject对象。并使用ClassObject对象信息动态创建一个外部ClassObject对象的实例instance。 ⑨ TypeManager对象使用instance和MethodObject对象信息调用instance的动态方法。instance把执行结果返回给Action对象。 ⑩ Action对象把执行结果返回给外部应用。 图10 PlugPlatform实现的顺序图 其中Plus工厂模式采用了Factory模式。对于Assembly的生成采用了Singleton模式。 4.4 异常处理 由于应用程序中有很多不可预料的问题,本平台在很多地方都有可能出现人为错误,如找不到配置文件;配置文件的格式不对,不能解析配置文件;类或接口名称写错了,不能实例化类;方法名称写错了,不能执行方法等等。增加异常处理主要是增强其容错性,在这里就不做更多的说明。 5.应用实例 本例子程序主要有三个方面组成:XML配置文件、外部DLL文件和PlusPlatform调用代码。 5.1 XML配置文件 采用的XML配置文件有两个,一个是针对类对象的XML配置文件,一个是针对接口对象的配置文件。 其中类对象的XML配置文件: 接口对象的配置文件与类对象配置文件基本相同,只不过配置信息中由类换成了接口: 5.2 DLL文件内容 其编译的DLL文件为UserLibrary.dll,该dll文件包括两个类和一个接口,其内部代码为: 5.3 调用插件平台代码 调用代码也分为两类,一类是针对类对象处理的,代码如下: 另一类是针对接口对象处理,代码如下: 可以对返回的DynamicArrayObject做分解查看,满足设计要求。 6.结束语 反射机制结合动态数组很好地解决了应用软件的后期维护和升级。对于应用软件的变化,可不改动任何现有的程序,只要修改XML配置文件的相应对象名称和加载
public class MethodObject {
private ClassObject classobject = null;
private InterfaceObject interfaceobject = null;
private DllFileObject dllfileobject = null;
private string name = string.Empty;
private string simplename = string.Empty;
private string implementname = string.Empty;
public string MethodName {get { return this.name; } set { this.name = value; } }
public string SimpleName { get { return this.simplename; }set { this.simplename = value; } }
public string ImplementName {get { return this.implementname; }set { this.implementname = value; }}
public ClassObject ClassObject {get { return this.classobject; }set { this.classobject = value; }}
public InterfaceObject InterfaceObject {get {return this.interfaceobject; }set {this.interfaceobject = value; }}
public DllFileObject DLLFileObject{ get { return this.dllfileobject; } set { this.dllfileobject = value; }}
}public DynamicArrayObject addObject(DataValueObject obj) {
if (Objects == null) {
Objects = new DataValueObject[1];
Objects[0] = obj;
return this; }
else {
DataValueObject[] objectList = new DataValueObject[Length + 1];
for (int i = 0; i < Length; i++) { objectList[i] = Objects[i]; }
objectList[Length] = obj;
Objects = objectList;
return this;}
}public DynamicArrayObject deleteObject(int idx) {
if (Objects == null) return this;
else {
if (idx >= 0 && idx < Length && Length > 1) {
DataValueObject[] objectList = new DataValueObject[Length - 1];
for (int i = 0; i < idx; i++) {objectList[i] = Objects[i];}
for (int i = idx + 1; i < Length; i++) {objectList[i - 1] = Objects[i]; }
Objects = objectList;
return this; }
else {
if (idx == 0 && Length <= 1) {
Objects = null;
return this; }
else return this; } }
}
public object InvokeClassMethod(String className, object[] objectArgs, String methodName, object[] methodArgs) {
Object[] newArgs = new Object[methodArgs.Length];
Object thisObject = new Object();
Type type = CreateType(className);
MethodInfo[] methods = type.GetMethods();
Object instance = CreateObject(type, objectArgs);
foreach (MethodInfo m in methods) {
if (m.Name == methodName) {
newArgs = ConvertArgsType(m, methodArgs);
try {
if (!m.IsStatic) thisObject = m.Invoke(instance, newArgs); //非静态方法,使用类实例调用
else thisObject = m.Invoke(null, newArgs);
return thisObject;}
catch (Exception e){throw new PlusException("不能动态调用方法,原因:" + e.Message, e); }}
}
return thisObject;
}<?xml version="1.0" encoding="utf-8" ?>
<PlugPlatformResource>
<DllFile name="UserLibrary.dll" filepath ="/" objectType ="class">
<classobject name="UserLibrary.UserTest1" >
<Methodobject>testAction01</Methodobject>
<Methodobject>testAction02</Methodobject>
<Methodobject>testAction03</Methodobject>
</classobject>
<classobject name="UserLibrary.UserTest2" >
<Methodobject>testAction01</Methodobject>
</classobject>
</DllFile>
</PlugPlatformResource><?xml version="1.0" encoding="utf-8" ?>
<PlugPlatformResource>
<DllFile name="UserLibrary.dll" filepath ="/" objectType ="interface">
<interfaceobject name="InterfaceTest1" implement="UserLibrary.UserTest2" >
<Methodobject>testAction01</Methodobject>
</interfaceobject>
</DllFile>
</PlugPlatformResource>public class UserTest1 {
public DynamicArrayObject testAction01(DynamicArrayObject outObject) {
DynamicArrayObject thisObject = new DynamicArrayObject();
//分解DynamicArrayObject
DataValueObject do1 = null;
string ls = null;
for (int i = 0; i < outObject.Length; i++) {
do1 = outObject.getObject(i);
ls += (String)do1.getDataValue(); }
DataValueObject do2 = new DataValueObject();
do2.setDataType(do1.getDataType()).setDataValue(ls);
//组装DynamicArrayObject,返回DynamicArrayObject
thisObject.addObject(do2);
return thisObject;
}
public DynamicArrayObject testAction02(DynamicArrayObject outObject) {
return outObject; }
}
public class UserTest2 : InterfaceTest1 {
public DynamicArrayObject testAction01(DynamicArrayObject outObject) {
DynamicArrayObject thisObject = new DynamicArrayObject();
//分解DynamicArrayObject
DataValueObject do1 = null;
string ls = null;
for (int i = 0; i < outObject.Length; i++) {
do1 = outObject.getObject(i);
ls += (String)do1.getDataValue(); }
DataValueObject do2 = new DataValueObject();
do2.setDataType(do1.getDataType()).setDataValue(ls);
//组装DynamicArrayObject,返回DynamicArrayObject
thisObject.addObject(do2);
return thisObject;
}
public DynamicArrayObject testAction02(DynamicArrayObject outObject){
return outObject;}
}
public interface InterfaceTest1 {
DynamicArrayObject testAction01(DynamicArrayObject outObject);
} DynamicArrayObject thisObject = new DynamicArrayObject();
DataValueObject do1 = new DataValueObject();
DataValueObject do2 = new DataValueObject();
do1.setDataType("string").setDataValue("类测试:第一个对象值.");
do2.setDataType("string").setDataValue("第二个对象值.");
thisObject.addObject(do1).addObject(do2);
string dllFile = Application.StartupPath + "\DllClassFile.xml";
PlugFactory factory = PlusConfig.BuildFactory(dllFile);
IAction action = factory.CreatAction();
DynamicArrayObject outputObject = action.Execute("UserLibrary.UserTest1.testAction01", thisObject);DynamicArrayObject thisObject = new DynamicArrayObject();
DataValueObject do1 = new DataValueObject();
DataValueObject do2 = new DataValueObject();
do1.setDataType("string").setDataValue("接口测试:第一个对象值.");
do2.setDataType("string").setDataValue("第二个对象值.");
thisObject.addObject(do1).addObject(do2);
string dllFile = Application.StartupPath + "\DllInterfaceFile.xml";
PlugFactory factory = PlusConfig.BuildFactory(dllFile);
IAction action = factory.CreatAction();
DynamicArrayObject outputObject = action.Execute("InterfaceTest1.testAction01", thisObject);发表评论
-
美了哭了,AduSkin是我见过最好看的WPF控件库
2020-03-14 19:58 577原文地址:https://www.pianshen.com/ ... -
使用NSSM把.Net Core部署至 Windows 服务
2019-10-16 08:52 461转自:https://www.cnblogs.com/emr ... -
玩转ASP.NET Core中的日志组件
2019-01-06 16:11 659转自:https://www.cnblogs.com/ ... -
CentOS 7 部署 ASP.NET Core 应用程序
2019-01-04 13:56 463CentOS 7 部署 ASP.NET Core 应用程序 ... -
GeckoWebBrowser实现自定义打印
2018-12-22 00:26 745nsIWebBrowserPrint print ... -
Winform桌面程序如何使用Geckofx45
2018-12-05 14:50 2269NuGet管理GeckoFx PM> Inst ... -
NLog的介绍使用
2018-09-28 17:36 993NLog是什么 NLog是一个基于.NET平台编写的类库, ... -
常用的.net开源项目
2018-09-28 17:01 424常用 Json.NET:Json.Net ... -
高效实用的.NET开源项目
2018-09-28 16:58 4441.Akka.NET: 概述:更轻松地构建强大的并发 ... -
OCR框架整理
2017-08-16 09:29 529泰比ABBYY OCR SDK开发包 Asp ... -
使用Aspose.word导出word报告
2017-04-09 11:49 1547最近在做一个.net项目需要实现网站导出word报告功能,最 ... -
利用Aspose.Words 完美生成word试卷
2017-04-09 11:43 5103aspose.word生成word文档 public cl ... -
java使用wsimport生成服务代码时报错的解决办法
2017-01-18 11:27 1629今天遇到了WebService的问题:在使用jdk命令wsi ... -
OCR文字识别系统
2016-12-21 22:14 377慧视OCR文字识别系统 增值税专用发票扫描识别系统 ... -
asprise-ocr:验证码识别
2016-12-21 22:17 699asprise-ocr http://asprise ... -
jieba中文分词的.NET版本:jieba.NET
2015-09-10 09:36 756转自:http://www.cnblogs.com/ande ... -
通用身份验证类:AuthenticationCookie
2015-09-06 09:00 678/// <summary> /// C ... -
KwCombinatorics排列组合类库
2015-08-06 17:34 557KwCombinatorics类库的主页是:http: ... -
CefSharp嵌入式浏览器
2015-08-06 15:21 999日常开发中,我们需要将一些Web页面嵌入到桌面客户端软件中。 ... -
C#开源框架(整理)
2015-07-27 09:32 2300http://www.cnblogs.com/c ...
相关推荐
c# 插件机制的实现(如何通过插件的机制,实现动态功能扩展)
用一个最精炼的例子介绍了反射机制实现插件模式的原理!
c# 简单的IOC实现框架,包含通过配置,利用c#反射机制来动态注入
C#,利用反射动态创建对象 C#,利用反射动态创建对象 C#,利用反射动态创建对象 C#,利用反射动态创建对象
利用.NET反射机制,只要程序实现了预定的接口,就可以被主程序实例化,并调用显示方法将其显示而不需要知道其类名。支持XML格式的配置文件。
C#反射机制源码学习.rar C#反射机制源码学习.rar C#反射机制源码学习.rar
实用结合C#反射实现动态定时器定时任务工具,可结合XML配置文档,实现独立动态的定时配置;用于定时任务执行、消息推送、WebService任务等;附件为程序源码。工具历经多项目验证,不足之处;欢迎交流指正!
C# 反射工厂示例C# 反射工厂示例C# 反射工厂示例C# 反射工厂示例C# 反射工厂示例C# 反射工厂示例C# 反射工厂示例C# 反射工厂示例C# 反射工厂示例C# 反射工厂示例
C#里面详解反射机制, Assembly类可以获得程序集的信息,也可以动态的加载程序集,以及在程序集中查找类型信息,并创建该类型的实例。 使用Assembly类可以降低程序集之间的耦合,有利于软件结构的合理化。
反射(Reflection)是.NET中的重要机制,通过放射,可以在运行时获得.NET中每一个类型(包括类、结构、委托、接口和枚举等)的成员,包括方法、属性、事件,以及构造函数等。还可以获得每个成员的名称、限定符和参数...
c#插件模式 ——例子(利用反射机制) foreach (ListViewItem item in listView1.Items) { if (item.Checked) { Assembly assembly = Assembly.LoadFile(Path.Combine(pluginpath, "TestComWin.dll")); // ...
C#插件反射 ReflectionPlugin.cs
项目中涉及到Asp.Net扩展性,考虑了两种扩展方法,一种是使用Web Services实现,另一... 使用插件机制来扩展B/S程序,主要需要实现两个功能,首先是动态编译插件中的C#代码,然后利用反射来执行插件已经编译的C#代码。
使用C++模拟C#中的“反射”的动态创建功能,实现对象的动态加载.在自定义类中使用DECLARE_REFLECTION声明反射;使用IMPLEMENT_REFLECTION实现反射。 程序运行过程可以使用Assembly::CreateInstance动态创建对象(可以...
获取传入类型拥有的方法名称 获取一个方法的所有参数信息
利用Type动态创建类实例(C#反射)可以演变抽象工厂
c# 利用反射来使得数据库种的字段直接绑定到控件上
C#反射机制详细概括,详细说明C#反射原理及其重要应用。
C#插件模式,反射加载DLL,INI文件配置
利用AppDomain实现对DLL的动态加载和卸载。