如何为C#创建插件插件

时间:2015-05-05 13:57:21

标签: c# mef

每个人都在说使用MEF。就是这样。现在,对于我的生活,我无法弄清楚如何使用它。

我下载了一个示例项目:

public interface ICalculator
    {
        String Calculate(String input);
    }

    public interface IOperation
    {
        int Operate(int left, int right);
    }

    public interface IOperationData
    {
        Char Symbol { get; }
    }

    [Export(typeof(IOperation))]
    [ExportMetadata("Symbol", '+')]
    class Add : IOperation
    {
        public int Operate(int left, int right)
        {
            return left + right;
        }
    }

    [Export(typeof(IOperation))]
    [ExportMetadata("Symbol", '-')]
    class Subtract : IOperation
    {

        public int Operate(int left, int right)
        {
            return left - right;
        }

    }



    [Export(typeof(ICalculator))]
    class MySimpleCalculator : ICalculator
    {
        [ImportMany]
        IEnumerable<Lazy<IOperation, IOperationData>> operations;

        public String Calculate(String input)
        {
            int left;
            int right;
            Char operation;
            int fn = FindFirstNonDigit(input); //finds the operator
            if (fn < 0) return "Could not parse command.";

            try
            {
                //separate out the operands
                left = int.Parse(input.Substring(0, fn));
                right = int.Parse(input.Substring(fn + 1));
            }
            catch
            {
                return "Could not parse command.";
            }

            operation = input[fn];

            foreach (Lazy<IOperation, IOperationData> i in operations)
            {
                if (i.Metadata.Symbol.Equals(operation)) return i.Value.Operate(left, right).ToString();
            }
            return "Operation Not Found!";
        }

        private int FindFirstNonDigit(String s)
        {

            for (int i = 0; i < s.Length; i++)
            {
                if (!(Char.IsDigit(s[i]))) return i;
            }
            return -1;
        }


    }


    class Program
    {
        private CompositionContainer _container;

        [Import(typeof(ICalculator))]
        public ICalculator calculator;


        private Program()
        {
            //An aggregate catalog that combines multiple catalogs
            var catalog = new AggregateCatalog();
            //Adds all the parts found in the same assembly as the Program class
            catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly));
            catalog.Catalogs.Add(new DirectoryCatalog(@"C:\Users\Lee Yi\Desktop\Everything, for the moment\Coding\Simple Calculator MEF Application\C#\SimpleCalculator3\Extensions"));


            //Create the CompositionContainer with the parts in the catalog
            _container = new CompositionContainer(catalog);

            //Fill the imports of this object
            try
            {
                this._container.ComposeParts(this);
            }
            catch (CompositionException compositionException)
            {
                Console.WriteLine(compositionException.ToString());
            }
        }


        static void Main(string[] args)
        {
            Program p = new Program(); //Composition is performed in the constructor
            String s;
            Console.WriteLine("Enter Command:");
            while (true)
            {
                s = Console.ReadLine();
                Console.WriteLine(p.calculator.Calculate(s));
            }


        }
    }

我不明白的是,所有这些都是在同一个文件中定义的。那我怎么能改变像

这样的不同代码元素呢?
    [Export(typeof(IOperation))]
    [ExportMetadata("Symbol", '+')]
    class Add : IOperation
    {
        public int Operate(int left, int right)
        {
            return left + right;
        }
    }

以另一个.dll为例?

就我而言,我在System.Windows.Forms.Button类中使用此功能,我有类似这样的内容:

class SpeakButton : Button
{ 
    public SpeakButton()
    { //Constructor that adds the speech handler automatically
        this.Click += new EventHandler(Speak_Button);
    }

    private void Speak_Button(object sender, EventArgs e)
    {
        Speaking.Speak((sender as Button).Text);
    }
}

但是我想让Speaking.Speak()函数(它实际上调用SpeechSynthesizer的SpeakAsync函数)是可选的,可以作为单独的.dll文件安装吗?

任何人都可以解释我将如何做这件事吗?

Speaking类定义如下:

class Speaking
{
    //Private fields (Hiding implementation code)
    private static SpeechSynthesizer Speaker = new SpeechSynthesizer();
    private static State state = State.Disabled;

    //Public properties
    public static int Volume
    {
        get { return Speaker.Volume; }
        set { Speaker.Volume = value; }
    }

    public static int Rate
    {
        get { return Speaker.Rate; }
        set { Speaker.Rate = value; }
    }

    public static State Speech
    {
        get { return state; }
        set { state = value; }
    }

    public static VoiceGender Gender
    {
        get { return Speaker.Voice.Gender; }
        set { Speaker.SelectVoiceByHints(value); }
    }

    public enum State { Enabled = 1, Disabled = 0 };

    public static void Speak(object message)
    { //Say stuff
        if (state == State.Disabled) { return; }
        Speaker.SpeakAsync(message.ToString());
    }

    public static void Cancel() { Speaker.SpeakAsyncCancelAll(); }
}

1 个答案:

答案 0 :(得分:0)

MEF无法正常使用静态类,因此我无法评论您的特定用例。如果要将代码迁移到另一个DLL以获得插件体系结构,则可以使用三个不同的程序集,即主EXE,包含插件接口的程序集和插件程序集。主要EXE和插件程序集引用插件接口程序集。例如:

主要EXE

  static class Program
  {
    static void Main()
    {
      Controller win = new Controller();
      win.ListPlugins();
    }
  }

  public class Controller
  {
    [System.ComponentModel.Composition.ImportMany]
    IEnumerable<Lazy<Interface.IPlugin>> plugins;

    System.ComponentModel.Composition.Hosting.CompositionContainer compContainer;

    public Controller()
    {
      var catalog = GetMefCatalogs();
      this.compContainer = new System.ComponentModel.Composition.Hosting.CompositionContainer(catalog);

      try
      {
        System.ComponentModel.Composition.AttributedModelServices.ComposeParts(this.compContainer, this);
      }
      catch (System.Reflection.ReflectionTypeLoadException ex)
      {
        foreach (Exception inner in ex.LoaderExceptions)
        {
          Console.WriteLine("Reflection error: {0}", inner.Message);
        }
      }
      catch (System.ComponentModel.Composition.CompositionException ex)
      {
        Console.WriteLine(ex.ToString());
      }

    }

    public void ListPlugins()
    {
      foreach (var pv in this.plugins)
      {
        Console.WriteLine(pv.Value.Name);
      }
    }

    public static System.ComponentModel.Composition.Hosting.AggregateCatalog GetMefCatalogs()
    {
      var catalog = new System.ComponentModel.Composition.Hosting.AggregateCatalog();
      // You should probably use these two lines, or the following lines, but not both.  If you use
      // this option you will need to ensure that the libs are all in the same directory.
      catalog.Catalogs.Add(new System.ComponentModel.Composition.Hosting.DirectoryCatalog(System.IO.Path.GetDirectoryName(System.AppDomain.CurrentDomain.BaseDirectory), "*.dll"));
      catalog.Catalogs.Add(new System.ComponentModel.Composition.Hosting.DirectoryCatalog(System.IO.Path.GetDirectoryName(System.AppDomain.CurrentDomain.BaseDirectory), "*.exe"));
      //foreach (var assembly in System.AppDomain.CurrentDomain.GetAssemblies())
      //{
      //  if (!assembly.FullName.StartsWith("System", StringComparison.OrdinalIgnoreCase) &&
      //    !assembly.FullName.StartsWith("Microsoft", StringComparison.OrdinalIgnoreCase) &&
      //    !assembly.FullName.StartsWith("mscorlib", StringComparison.OrdinalIgnoreCase) &&
      //    !assembly.FullName.StartsWith("Accessibility", StringComparison.OrdinalIgnoreCase) &&
      //    !assembly.FullName.StartsWith("vshost32", StringComparison.OrdinalIgnoreCase))
      //  {
      //    Console.WriteLine("Found assembly: " + assembly.FullName);
      //    catalog.Catalogs.Add(new System.ComponentModel.Composition.Hosting.AssemblyCatalog(assembly));
      //  }
      //}
      return catalog;
    }
  }

界面装配

  public interface IPlugin
  {
    string Name { get; }
    double GetRandom();
  }

插件程序集

  [System.ComponentModel.Composition.Export(typeof(Interface.IPlugin))]
  public class Plugin : Interface.IPlugin
  {
    private Random random;
    public Plugin()
    {
      this.random = new Random(DateTime.Now.Ticks.GetHashCode());
    }

    public string Name
    {
      get { return "Plugin"; }
    }

    public double GetRandom()
    {
      return this.random.NextDouble() * 10;
    }
  }

当然,您需要在插件和主EXE程序集(但不是插件程序集)中添加对System.ComponentModel.Composition的引用才能使用MEF。