.NET提供的反射(Reflection)機(jī)制可以很方便的加載插件。本文提供一種方法,,可以靈活的正確的載入所需的插件,。
在.NET中,,一個(gè)完整的類型名稱的格式如"類型名,程序集名",。
例如:"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,,系統(tǒng)為自動(dòng)為其適配擴(kuò)展名(如System.dll或System.exe);
Version,、Culture,、PublicKeyToken為程序集的具體版本、文化背景,、簽名,,沒(méi)有特定要求,這些都可以省略,。
我們可以根據(jù)類型的名稱,,來(lái)動(dòng)態(tài)載入一個(gè)所需要的類型,。如:
stringtypeName="System.Configuration.NameValueSectionHandler,System";
Typet=Type.GetType(typeName);
Objectobj=Activator.CreateInstance(t);
//或
System.Configuration.NameValueSectionHandlerobj=(System.Configuration.NameValueSectionHandler)Activator.CreateInstance(t);
此時(shí),obj就是所需要的類型實(shí)例,。
通常的插件,,是需要實(shí)現(xiàn)一定的接口的類。因此,,在載入插件之前,,需要確定該插件類型是否是合適的。
比如,,一個(gè)插件的接口為IPlugin,,那么我們可以用如下方式來(lái)識(shí)別:
stringinterfaceName=typeof(IPlugin).FullName;
stringtypeName="Muf.MyPlugin,MyPlugin";
Typet=Type.GetType(typeName);
if(t==null
||!t.IsClass
||!t.IsPublic
||t.GetInterface(interfaceName)==null)
{
returnnull;//不是所需要的插件
}
總結(jié)上述代碼,我們可以做出通用的加載插件的代碼:
/**////<summary>
///動(dòng)態(tài)裝載并創(chuàng)建類型,,該類型擁有指定接口
///</summary>
///<paramname="className">類型名稱</param>
///<paramname="interfaceName">指定的接口名稱</param>
///<paramname="param">指定構(gòu)造函數(shù)的參數(shù)(null或空的數(shù)組表示調(diào)用默認(rèn)構(gòu)造函數(shù))</param>
///<returns>返回所創(chuàng)建的類型(null表示該類型無(wú)法創(chuàng)建或找不到)</returns>
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載入任何所需的插件。
插件一般放在配置文件中,,并由程序讀入:
配置文件舉例(配置文件的使用參見(jiàn)我的相關(guān)隨筆):
<?xmlversion="1.0"encoding="utf-8"?>
<configuration>
<configSections>
<sectionname="Channels"type="Vmp.Configuration.ChannelsSectionHandler,Communication"/>
</configSections>
<Channels>
<channel
ChannelType="Vmp.Communication.TcpChannel,Communication"
TraceFile="d:\log\channel1.log"
Port="2020"MaxConnections="300"BufferSize="2048"
/>
</Channels>
</configuration>
代碼范例:
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)
{
//設(shè)置默認(rèn)的插件目錄
if(pluginDir==null||pluginDir=="")
pluginDir="./PlugIns";
//獲取插件接口名稱
stringinterfaceName=typeof(IPlugin).FullName;
//用于存放插件的數(shù)組
ArrayListarr=newArrayList();
//遍歷插件目錄(假設(shè)插件為dll文件)
foreach(stringfileinDirectory.GetFiles(pluginDir,"*.dll"))
{
//載入插件文件
Assemblyasm=Assembly.LoadFile(file);
//遍歷導(dǎo)出的插件類
foreach(Typetinasm.GetExportedTypes())
{
//載入插件,,如果插件不符合指定的接口,,則返回null
IPluginplugin=LoadObject(t.FullName,interfaceName,null)asIPlugin;
if(plugin!=null)
arr.Add(plugin);
}
}
//返回插件
return(IPlugin[])arr.ToArray(typeof(IPlugin));
}