C# 插件的书写

近期的一个项目中,需要用到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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不负初心

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值