C# 插件的书写
2015-08-14 09:14
821 查看
近期的一个项目中,需要用到C#书写插件,控制不同功能在编辑器中的实现,也是刚接手,慢慢领悟这种方法的美感。
用C#写个简单的插件的例子,类似于状态模式,提供一个计算器的接口,然后在派生类中实现不同的计算。最后通过加载这些实现的dll文件,来实现不同的运算
我们有不同的方法实现这个接口
Late Binding
写一个新的计算器实现方法,并将 其dll 放到文件中。然后可以让应用层能够使用这个新的函数使用了绑定机制。我们把所有的插件放入 “Plugins” 文件中,我们使用 static 类去管理绑定的中间结果。 使用lazy-load 并创建一个load 方法,所以可以得记录用层第一次使用插件的情况
执行的界面如下:
当reload() 在首次调用的时候,我们创建一个新的list,在应用程序运行的时候可能还会下载新的插件。此时需要清空现有的 calculator list。 然后我们将要laod 插件中所有的 assemblies
Attributes
使用 custom attribute decorator 来 标记实现icalculator, 使用一些identifier 在 attibute中,这样可以用来对插件排序或者过滤,后面GetPlugins() 函数中,有体现
Guts
loading 插件的 assemblies , 使用Assembly.LoadFile() 来把它们添加到一个list中
使用 Plug-in 结构
使用控制台app,可以方便的进行测试 如下
windows 应用层,有友好的界面
以上的工程,需要另外添加一个文件夹"Plugins",里面放入各种主程序需要用的插件。
工程文件 download:
github.com/codeAPmind/pluggable/tree/master/PluggableHostApplication
Reference:
http://www.c-sharpcorner.com/UploadFile/rmcochran/plug_in_architecture09092007111353AM/plug_in_architecture.aspx
用C#写个简单的插件的例子,类似于状态模式,提供一个计算器的接口,然后在派生类中实现不同的计算。最后通过加载这些实现的dll文件,来实现不同的运算
public interface ICalculator { int Calculate(int a, int b); char GetSymbol(); }
我们有不同的方法实现这个接口
class Divider:ICalculator { #region ICalculator Members public int Calculate(int a, int b) { return a / b; } public char GetSymbol() { return '/'; } #endregion }我们可以提供一个默认的除法给主机,同时允许其他的实现插入到主机,这种模式也容易进行单元测试。
public class CalculatorHost { public CalculatorHost(ICalculator calculator) { m_calculator = calculator; } public CalculatorHost() : this(new Divider()) { } private int m_x, m_y; private ICalculator m_calculator; public int X { get { return m_x; } set { m_x = value; } } public int Y { get { return m_y; } set { m_y = value; } } public int Calculate() { return m_calculator.Calculate(m_x, m_y); } public override string ToString() { return string.Format("{0} {1} {2} = {3}", m_x.ToString(), m_calculator.GetSymbol(), m_y.ToString(), m_calculator.Calculate(m_x, m_y)); } }
Late Binding
写一个新的计算器实现方法,并将 其dll 放到文件中。然后可以让应用层能够使用这个新的函数使用了绑定机制。我们把所有的插件放入 “Plugins” 文件中,我们使用 static 类去管理绑定的中间结果。 使用lazy-load 并创建一个load 方法,所以可以得记录用层第一次使用插件的情况
public static class CalculatorHostProvider { private static List<CalculatorHost> m_calculators; public static List<CalculatorHost> Calculators { get { if (null == m_calculators) Reload(); return m_calculators; } } }
执行的界面如下:
当reload() 在首次调用的时候,我们创建一个新的list,在应用程序运行的时候可能还会下载新的插件。此时需要清空现有的 calculator list。 然后我们将要laod 插件中所有的 assemblies
public static void Reload() { if (null == m_calculators) m_calculators = new List<CalculatorHost>(); else m_calculators.Clear(); m_calculators.Add(new CalculatorHost()); // load the default List<Assembly> plugInAssemblies = LoadPlugInAssemblies(); List<ICalculator> plugIns = GetPlugIns(plugInAssemblies); foreach (ICalculator calc in plugIns) { m_calculators.Add(new CalculatorHost(calc)); } }
Attributes
使用 custom attribute decorator 来 标记实现icalculator, 使用一些identifier 在 attibute中,这样可以用来对插件排序或者过滤,后面GetPlugins() 函数中,有体现
[AttributeUsage(AttributeTargets.Class)] public class CalculationPlugInAttribute : Attribute { public CalculationPlugInAttribute(string description) { m_description = description; } private string m_description; public string Description { get { return m_description; } set { m_description = value; } } }
Guts
loading 插件的 assemblies , 使用Assembly.LoadFile() 来把它们添加到一个list中
private static List<Assembly> LoadPlugInAssemblies() { DirectoryInfo dInfo = new DirectoryInfo(Path.Combine(Environment.CurrentDirectory, "Plugins")); FileInfo[] files = dInfo.GetFiles("*.dll"); List<Assembly> plugInAssemblyList = new List<Assembly>(); if (null != files) { foreach (FileInfo file in files) { plugInAssemblyList.Add(Assembly.LoadFile(file.FullName)); } } return plugInAssemblyList; }然后,使用下载后的list并 get all types implement out ICalculator,
使用 Plug-in 结构
使用控制台app,可以方便的进行测试 如下
static void Main(string[] args) { int x = 34, y = 56; Console.WriteLine(String.Format("x={0} y={1}", x.ToString(), y.ToString())); foreach (CalculatorHost calculator in CalculatorHostProvider.Calculators) { calculator.X = x; calculator.Y = y; Console.WriteLine(calculator.ToString()); } Console.ReadLine(); }
windows 应用层,有友好的界面
private void Form1_Load(object sender, EventArgs e) { m_cbCalculation.DisplayMember = "Operator"; m_cbCalculation.DataSource = CalculatorHostProvider.Calculators; }
以上的工程,需要另外添加一个文件夹"Plugins",里面放入各种主程序需要用的插件。
工程文件 download:
github.com/codeAPmind/pluggable/tree/master/PluggableHostApplication
Reference:
http://www.c-sharpcorner.com/UploadFile/rmcochran/plug_in_architecture09092007111353AM/plug_in_architecture.aspx
相关文章推荐
- C#中enum类型
- 编写高质量代码改善C#程序的157个建议——建议20:使用泛型集合代替非泛型集合
- 插件式程序开发及其应用(C#)
- 编写高质量代码改善C#程序的157个建议——建议19:使用更有效的对象和集合初始化
- C#中动态加载和卸载DLL
- 编写高质量代码改善C#程序的157个建议——建议18:foreach不能代替for
- C# 配置文件的使用
- C#引用类型
- C# 动态加载程序集
- C#动态加载/卸载Assembly的解决方案
- C# 它 抽象类和接口
- 编写高质量代码改善C#程序的157个建议——建议17:多数情况下使用foreach进行循环遍历
- C#判断对象相等
- 在C#中使用消息队列RabbitMQ
- 编写高质量代码改善C#程序的157个建议——建议16:元素数量可变的情况下不应使用数组
- C#.NET开源反编译工具ILSpy下载
- C#基于委托的带参数的消息传递设计
- C#实现DataGridView控件行列互换的方法
- c# list排序的三种实现方式
- C# 字符串的长度问题