.NET提供的反射(Reflection)机制可以很方便的加载插件。本文提供一种方法,可以灵活的正确的载入所需的插件。 在.NET中,一个完整的类型名称的格式如"类型名,程序集名"。 例如:"System.Configuration.NameValueSectionHandler,System,Version=1.0.3300.0, Culture=neutral,PublicKeyToken=b77a5c561934e089"。 类型名为:System.Configuration.NameValueSectionHandler,这是带名字空间的完整类型名。 你也可以使用该类型的FullName得到。 如:stringtypeName=typeof(NameValueSectionHandler).FullName; 程序集名为:"System,Version=1.0.3300.0,Culture=neutral,PublicKeyToken=b77a5c561934e089", 程序集名为System,系统为自动为其适配扩展名(如System.dll或System.exe); Version、Culture、PublicKeyToken为程序集的具体版本、文化背景、签名,没有特定要求,这些都可以省略。 我们可以根据类型的名称,来动态载入一个所需要的类型。如: stringtypeName="System.Configuration.NameValueSectionHandler,System"; Typet=Type.GetType(typeName); Objectobj=Activator.CreateInstance(t); //或 System.Configuration.NameValueSectionHandlerobj=(System.Configuration.NameValueSectionHandler)Activator.CreateInstance(t);
此时,obj就是所需要的类型实例。
通常的插件,是需要实现一定的接口的类。因此,在载入插件之前,需要确定该插件类型是否是合适的。 比如,一个插件的接口为IPlugin,那么我们可以用如下方式来识别: stringinterfaceName=typeof(IPlugin).FullName; stringtypeName="Muf.MyPlugin,MyPlugin"; Typet=Type.GetType(typeName); if(t==null ||!t.IsClass ||!t.IsPublic ||t.GetInterface(interfaceName)==null) { returnnull;//不是所需要的插件 } 总结上述代码,我们可以做出通用的加载插件的代码:
/**//// ///动态装载并创建类型,该类型拥有指定接口 /// ///类型名称 ///指定的接口名称 ///指定构造函数的参数(null或空的数组表示调用默认构造函数) ///返回所创建的类型(null表示该类型无法创建或找不到) publicstaticobjectLoadObject(stringclassName,stringinterfaceName,object[]param) { try { Typet=Type.GetType(className);
if(t==null ||!t.IsClass ||!t.IsPublic ||t.IsAbstract ||t.GetInterface(interfaceName)==null) { returnnull; } objecto=Activator.CreateInstance(t,param); if(o==null) { returnnull; } returno; } catch(Exceptionex) { returnnull; } } 以后,我们就可以使用LoadObject载入任何所需的插件。
插件一般放在配置文件中,并由程序读入: 配置文件举例(配置文件的使用参见我的相关随笔):
ChannelType="Vmp.Communication.TcpChannel,Communication" TraceFile="d:\log\channel1.log" Port="2020"MaxConnections="300"BufferSize="2048" />
代码范例:
privateArrayListchannelsList=newArrayList(); privateLoadChannels() { ArrayListchannelsConfig=(ArrayList)ConfigurationSettings.GetConfig("Channels"); foreach(HashtableconfiginchannelsConfig) { stringchannelType=(string)config["ChannelType"]; IChannelchannel=(IChannel)CommonUtils.LoadObject(channelType,typeof(IChannel).FullName,newobject[]{config}); if(channel==null) continue; channelsList.Add(channel); } 也可以遍历指定的插件目录,并载入所有符合要求的插件,例如: publicIPlugin[]LoadAllPlugIn(stringpluginDir) { //设置默认的插件目录 if(pluginDir==null||pluginDir=="") pluginDir="./PlugIns"; //获取插件接口名称 stringinterfaceName=typeof(IPlugin).FullName; //用于存放插件的数组 ArrayListarr=newArrayList(); //遍历插件目录(假设插件为dll文件) foreach(stringfileinDirectory.GetFiles(pluginDir,"*.dll")) { //载入插件文件 Assemblyasm=Assembly.LoadFile(file); //遍历导出的插件类 foreach(Typetinasm.GetExportedTypes()) { //载入插件,如果插件不符合指定的接口,则返回null IPluginplugin=LoadObject(t.FullName,interfaceName,null)asIPlugin; if(plugin!=null) arr.Add(plugin); } } //返回插件 return(IPlugin[])arr.ToArray(typeof(IPlugin)); }
|