靈活正確的實現.NET插件機制

 .NET 提供的反射(Reflection)機制可以很方便的加載插件。本文提供一種方法,可以靈活的正確的載入所需的插件。


  在.NET中,一個完整的類型名稱的格式如 "類型名, 程序集名"。

例如:"System.Configuration.NameValueSectionHandler, System, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"。

  • 類型名爲:System.Configuration.NameValueSectionHandler,這是帶名字空間的完整類型名。
    你也可以使用該類型的FullName得到。
    如:string typeName = typeof(NameValueSectionHandler).FullName;
  • 程序集名爲:"System, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
    程序集名爲System,系統爲自動爲其適配擴展名(如System.dll或System.exe);
    Version、Culture、PublicKeyToken爲程序集的具體版本、文化背景、簽名,沒有特定要求,這些都可以省略。

  我們可以根據類型的名稱,來動態載入一個所需要的類型。如:

None.gifstring typeName = "System.Configuration.NameValueSectionHandler, System";
None.gifType t 
= Type.GetType(typeName);
None.gifObject obj 
= Activator.CreateInstance(t);
None.gif
//
None.gif
System.Configuration.NameValueSectionHandler obj = (System.Configuration.NameValueSectionHandler)Activator.CreateInstance(t);
None.gif
None.gif

  此時,obj 就是所需要的類型實例。

  通常的插件,是需要實現一定的接口的類。因此,在載入插件之前,需要確定該插件類型是否是合適的。

  比如,一個插件的接口爲 IPlugin,那麼我們可以用如下方式來識別:

string interfaceName = typeof(IPlugin).FullName;
string typeName = "Muf.MyPlugin, MyPlugin";
Type t 
= Type.GetType(typeName);
              
if (  t == null 
  
|| !t.IsClass
  
|| !t.IsPublic 
  
||  t.GetInterface(interfaceName) == null)
{
 
return null// 不是所需要的插件
}

  總結上述代碼,我們可以做出通用的加載插件的代碼:

ExpandedBlockStart.gif/// <summary>
InBlock.gif
/// 動態裝載並創建類型,該類型擁有指定接口
InBlock.gif
/// </summary>
InBlock.gif
/// <param name="className">類型名稱</param>
InBlock.gif
/// <param name="interfaceName">指定的接口名稱</param>
InBlock.gif
/// <param name="param">指定構造函數的參數(null或空的數組表示調用默認構造函數)</param>
ExpandedBlockEnd.gif
/// <returns>返回所創建的類型(null表示該類型無法創建或找不到)</returns>

None.gifpublic static object LoadObject(string className, string interfaceName, object[] param)
ExpandedBlockStart.gif
{
InBlock.gif 
try
ExpandedSubBlockStart.gif 
{
InBlock.gif  Type t 
= Type.GetType(className);
InBlock.gif              
InBlock.gif  
if ( t == null 
InBlock.gif   
|| !t.IsClass
InBlock.gif   
||  !t.IsPublic 
InBlock.gif   
||  t.IsAbstract
InBlock.gif   
||  t.GetInterface(interfaceName) == null)
ExpandedSubBlockStart.gif  
{
InBlock.gif   
return null;
ExpandedSubBlockEnd.gif  }

InBlock.gif
InBlock.gif  
object o = Activator.CreateInstance(t, param);
InBlock.gif  
if( o == null )
ExpandedSubBlockStart.gif  
{
InBlock.gif   
return null;
ExpandedSubBlockEnd.gif  }

InBlock.gif    
InBlock.gif  
return o;
ExpandedSubBlockEnd.gif }

InBlock.gif 
catch( Exception ex )
ExpandedSubBlockStart.gif 
{
InBlock.gif  
return null;
ExpandedSubBlockEnd.gif }

ExpandedBlockEnd.gif}

None.gif
None.gif

  以後,我們就可以使用LoadObject載入任何所需的插件。

  插件一般放在配置文件中,並由程序讀入:

  配置文件舉例(配置文件的使用參見我的相關隨筆):

None.gif<?xml version="1.0" encoding="utf-8" ?>
None.gif
<configuration>
None.gif    
<configSections>
None.gif        
<section name="Channels" type="Vmp.Configuration.ChannelsSectionHandler, Communication" />
None.gif    </configSections>
None.gif    
None.gif    
<Channels>
None.gif        
<channel
None.gif            
ChannelType="Vmp.Communication.TcpChannel, Communication" 
None.gif            TraceFile
="d:/log/channel1.log"
None.gif            Port
="2020" MaxConnections="300" BufferSize="2048"
None.gif        
/>
None.gif    
</Channels>
None.gif
</configuration>

  代碼範例:

None.gifprivate ArrayList channelsList = new ArrayList();
None.gif
None.gif
private LoadChannels()
ExpandedBlockStart.gif
{
InBlock.gif    ArrayList channelsConfig 
= (ArrayList)ConfigurationSettings.GetConfig( "Channels" );
InBlock.gif    
foreach(Hashtable config in channelsConfig)
ExpandedSubBlockStart.gif    
{
InBlock.gif        
string channelType    = (string) config["ChannelType"];
InBlock.gif
ExpandedSubBlockStart.gif        IChannel channel 
= (IChannel) CommonUtils.LoadObject(channelType, typeof(IChannel).FullName, new object[]{config});
InBlock.gif        
if(channel == null)
InBlock.gif            
continue;
InBlock.gif
InBlock.gif        channelsList.Add(channel);
ExpandedSubBlockEnd.gif}

InBlock.gif

  也可以遍歷指定的插件目錄,並載入所有符合要求的插件,例如:

None.gifpublic IPlugin[] LoadAllPlugIn(string pluginDir)
ExpandedBlockStart.gif
{
InBlock.gif    
// 設置默認的插件目錄
InBlock.gif
    if(pluginDir == null || pluginDir == "")
InBlock.gif        pluginDir 
= "./PlugIns";
InBlock.gif
InBlock.gif    
// 獲取插件接口名稱
InBlock.gif
    string interfaceName = typeof(IPlugin).FullName;
InBlock.gif
InBlock.gif    
// 用於存放插件的數組
InBlock.gif
    ArrayList arr = new ArrayList();
InBlock.gif
InBlock.gif    
// 遍歷插件目錄(假設插件爲dll文件)
InBlock.gif
    foreach(string file in Directory.GetFiles(pluginDir, "*.dll"))
ExpandedSubBlockStart.gif    
{
InBlock.gif        
// 載入插件文件
InBlock.gif
        Assembly asm = Assembly.LoadFile(file);
InBlock.gif        
// 遍歷導出的插件類
InBlock.gif
        foreach(Type t in asm.GetExportedTypes())
ExpandedSubBlockStart.gif        
{
InBlock.gif            
// 載入插件,如果插件不符合指定的接口,則返回null
InBlock.gif
            IPlugin plugin = LoadObject(t.FullName, interfaceName, nullas IPlugin;
InBlock.gif
InBlock.gif            
if(plugin != null)
InBlock.gif                arr.Add(plugin);
ExpandedSubBlockEnd.gif        }

ExpandedSubBlockEnd.gif    }

InBlock.gif
InBlock.gif    
// 返回插件
InBlock.gif
    return (IPlugin[])arr.ToArray(typeof(IPlugin));
ExpandedBlockEnd.gif}
<script> var ad_cid; if (window.location.search.substring(1) != "") { ad_cid = window.location.search.substring(1); } else { ad_cid = 324; } </script> <script language=JavaScript> document.getElementById("ad1").innerHTML=document.getElementById("span_ad1").innerHTML; document.getElementById("span_ad1").innerHTML=""; </script> <script> if(document.getElementById("contentAdv")) { document.getElementById("contentAdv").innerHTML=document.getElementById("span_ad3").innerHTML; document.getElementById("span_ad3").innerHTML=""; } else { document.getElementById("ad3").innerHTML=document.getElementById("span_ad3").innerHTML; document.getElementById("span_ad3").innerHTML=""; }</script> <script language=JavaScript> document.getElementById("ad2").innerHTML=document.getElementById("span_ad2").innerHTML; document.getElementById("span_ad2").innerHTML=""; </script> <script language=JavaScript> document.getElementById("ad10").innerHTML=document.getElementById("span_ad10").innerHTML; document.getElementById("span_ad10").innerHTML=""; </script> <script language=JavaScript> document.getElementById("ad9").innerHTML=document.getElementById("span_ad9").innerHTML; document.getElementById("span_ad9").innerHTML=""; </script>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章