您的位置:首页 > 编程语言 > C#

C# 插件的书写

2015-08-14 09:14 821 查看
近期的一个项目中,需要用到C#书写插件,控制不同功能在编辑器中的实现,也是刚接手,慢慢领悟这种方法的美感。

用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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: