C#反射實現(xiàn)插件式開發(fā)的過程詳解
前言
插件式架構(gòu),一種全新的、開放性的、高擴展性的架構(gòu)體系。插件式架構(gòu)設(shè)計好處很多,把擴展功能從框架中剝離出來,降低了框架的復(fù)雜度,讓框架更容易實現(xiàn)。擴展功能與框架以一種很松的方式耦合,兩者在保持接口不變的情況下,可以獨立變化和發(fā)布。基于插件設(shè)計并不神秘,相反它比起一團泥的設(shè)計更簡單,更容易理解。
項目介紹
書寫4個插件類庫,分別傳參實現(xiàn)“加減乘除”運算,調(diào)用插件的客戶端采用Winform窗體程序。
目標框架: .NET Framework 4.6.1
項目架構(gòu)和窗體布局:

客戶端程序:
- PluginApp:反射調(diào)用插件
插件描述:
- PluginBase:規(guī)范插件的基類,定義抽象類,開發(fā)的插件的類需要繼承此類,代表遵守這個規(guī)范。
- CustomPlugInA:實現(xiàn)加法的插件
- CustomPlugInB:實現(xiàn)減法的插件
- CustomPlugInC:實現(xiàn)乘法的插件
- CustomPlugInD:實現(xiàn)除法的插件
代碼實現(xiàn)
插件基類
/// <summary>
///插件基類
/// </summary>
public abstract class Base
{
/// <summary>
/// 插件名稱
/// </summary>
/// <returns></returns>
public abstract string Name();
/// <summary>
/// 插件描述
/// </summary>
/// <returns></returns>
public abstract string Desc();
/// <summary>
/// 執(zhí)行方法
/// </summary>
/// <param name="param1">參數(shù)1</param>
/// <param name="param2">參數(shù)2</param>
/// <returns></returns>
public abstract string Run(int param1, int param2);
/// <summary>
/// 版本
/// </summary>
public string Version
{
get { return "1.0.0"; }
}
}PlugInA
public class PlugInA: Base
{
public override string Name()
{
return "PlugInA";
}
public override string Desc()
{
return "加法";
}
public override string Run(int param1,int param2)
{
return (param1 + param2) + "";
}
}
}PlugInB
public class PlugInB : Base
{
public override string Name()
{
return "PlugInB";
}
public override string Desc()
{
return "減法";
}
public override string Run(int param1, int param2)
{
return (param1 - param2) + "";
}
}PlugInC
public class PlugInC : Base
{
public override string Name()
{
return "PlugInC";
}
public override string Desc()
{
return "乘法";
}
public override string Run(int param1, int param2)
{
return (param1 * param2) + "";
}
}PlugInD
public class PlugInD : Base
{
public override string Name()
{
return "PlugInD";
}
public override string Desc()
{
return "除法";
}
public override string Run(int param1, int param2)
{
return (param1 / param2) + "";
}
}客戶端核心代碼:
public partial class FrmMain : Form
{
public FrmMain()
{
InitializeComponent();
dgrvPlugins.AutoGenerateColumns = false;
}
List<PluginModel> List = new List<PluginModel>();
readonly string PlugInPath = Application.StartupPath + "\\PlugIns";
/// <summary>
/// 載入插件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btLoadPlugins_Click(object sender, EventArgs e)
{
if (!Directory.Exists(PlugInPath))
{
Directory.CreateDirectory(PlugInPath);
}
List.Clear();
string[] files = Directory.GetFiles(PlugInPath);
foreach (string file in files)
{
if (file.ToLower().EndsWith(".dll"))
{
try
{
Assembly assembly = Assembly.LoadFrom(file);
Type[] types = assembly.GetTypes();
foreach (Type type in types)
{
if (type.BaseType.FullName == "PlugInBase.Base")
{
object obj = assembly.CreateInstance(type.FullName);
string name = type.GetMethod("Name").Invoke(obj, null).ToString();
string desc = type.GetMethod("Desc").Invoke(obj, null).ToString();
string version = type.GetProperty("Version").GetValue(obj).ToString();
List.Add(new PluginModel
{
Name = name,
Desc = desc,
Version = version,
type = type,
Obj = obj
});
}
}
}
catch (Exception ex)
{
throw ex;
}
}
}
dgrvPlugins.DataSource = new BindingList<PluginModel>(List);
}
/// <summary>
/// 打開插件目錄
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btOpenPluginDir_Click(object sender, EventArgs e)
{
Process.Start(PlugInPath);
}
/// <summary>
/// 執(zhí)行選中插件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btExcute_Click(object sender, EventArgs e)
{
//獲取選擇的插件信息
int index = dgrvPlugins.CurrentRow.Index;
object obj = List[index].Obj;
Type type = List[index].type;
//參數(shù)
object[] inParams = new object[2];
inParams[0] =Convert.ToInt32( dgrvPlugins.CurrentRow.Cells[2].Value);
inParams[1] = Convert.ToInt32(dgrvPlugins.CurrentRow.Cells[3].Value);
object value = type.GetMethod("Run").Invoke(obj, inParams);
MessageBox.Show(Convert.ToString(value),"結(jié)果",MessageBoxButtons.OK);
}
}項目配置
插件生成配置
編譯生成項目的時候需要注意,此處的調(diào)用插件是通過反射調(diào)用.dll中類和方法,所以首先要找到這個.dll的文件,所以此處我們在Winform客戶端程序下建立一個存放類庫dll的文件 PlugIns ,在插件類庫項目生成后事件命令中,填入如下命令:
copy /Y "$(TargetDir)$(ProjectName).dll" "$(SolutionDir)\PlugIns"
以上命令代表,在項目的類庫生成后,將類庫copy到解決方案的路徑子文件夾 PlugIns ,也就是我們建立存放自定義插件的文件夾。當然,如果不怕麻煩,每次生成后,手動復(fù)制到此文件夾也可以,直接復(fù)制到客戶端程序的 ..\bin\PlugIns 文件夾下。

插件路徑配置
全選這些類庫,把這些類庫設(shè)置為"如果較新則復(fù)制",這樣每次在編譯客戶端程序,如果自定義插件有更新,則同步會復(fù)制到 bin 目錄下

插件基類配置
插件基類提供了規(guī)范,需要在類庫的生成后事件,添加命令:
copy /Y "$(TargetDir)$(ProjectName).dll" "$(SolutionDir)\bin\Debug"
將生成的dll文件,拷貝到客戶端程序的 bin 路徑下

調(diào)用演示CustomPlugInA

CustomPlugInB

CustomPlugInC

CustomPlugInD

插件開發(fā)優(yōu)缺點
- 把擴展功能從框架中剝離出來,降低了框架的復(fù)雜度,讓框架更容易實現(xiàn)
- 宿主中可以對各個模塊解析,完成插件間、插件和主程序間的通信。
- 插件開發(fā)的可擴展性,靈活性比較高,而且可以進行定制化開發(fā)。
缺點
- 每一個插件被編譯成了dll,各模塊無法單獨運行,必須依托于主程序。
- 修改插件時,由于生成的是dll,無法快速直觀的查看修改以及調(diào)試。
- 每一個插件必須依賴于某一個規(guī)范。
到此這篇關(guān)于C#反射實現(xiàn)插件式開發(fā)的文章就介紹到這了,更多相關(guān)C#反射插件式開發(fā)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
DevExpress實現(xiàn)GridControl單元格編輯驗證的方法
這篇文章主要介紹了DevExpress實現(xiàn)GridControl單元格編輯驗證的方法,很實用的功能,需要的朋友可以參考下2014-08-08
C#操作LINQ to SQL組件進行數(shù)據(jù)庫建模的基本教程
這篇文章主要介紹了C#操作LINQ to SQL組件進行數(shù)據(jù)庫建模的基本教程,LINQ to SQL被集成在.NET框架之中,需要的朋友可以參考下2016-03-03
C#12中的Collection expressions集合表達式語法糖詳解
C#12中引入了新的語法糖來創(chuàng)建常見的集合,并且可以使用..來解構(gòu)集合,將其內(nèi)聯(lián)到另一個集合中,下面就跟隨小編一起學(xué)習(xí)一下C#12中這些語法糖的使用吧2023-11-11
使用C#實現(xiàn)將Excel轉(zhuǎn)換為Markdown表格
Markdown 表格廣泛應(yīng)用于技術(shù)文檔,README 文件以及各類靜態(tài)站點生成器中,本文將介紹如何使用 C# 將 Excel 文件轉(zhuǎn)換為 Markdown 表格,感興趣的小伙伴可以了解下2026-01-01
基于C#實現(xiàn)WinForm開發(fā)操作系統(tǒng)的文件管理系統(tǒng)代碼
基于C#的WinForm應(yīng)用程序來模擬操作系統(tǒng)文件管理系統(tǒng),可以幫助用戶在Windows環(huán)境下進行文件的創(chuàng)建、移動、刪除與搜索等操作,這種模擬工具有助于學(xué)習(xí)文件系統(tǒng)的工作原理以及測試和開發(fā)其他軟件項目2024-12-12
C# HttpClient Post參數(shù)同時上傳文件的實現(xiàn)
這篇文章主要介紹了C# HttpClient Post參數(shù)同時上傳文件的實現(xiàn),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-06-06

