MEF學習

一、   什麼是MEF

  MEF(Managed Extensibility Framework)是一個用於創建可擴展的輕型應用程序的庫。 應用程序開發人員可利用該庫發現並使用擴展,而無需進行配置。 擴展開發人員還可以利用該庫輕鬆地封裝代碼,避免生成脆弱的硬依賴項。 通過 MEF,不僅可以在應用程序內重用擴展,還可以在應用程序之間重用擴展。(摘自MSDN)

  我的理解:應用/插件均使用約定好的協議(接口)進行開發。系統將自動掃描指定文件夾,並按協議自動導入。

二、   MEF簡單例子

1、例子一

a、定義接口

 public interface DemoOneInterface   
    {
        void Send(string msg);
    }

b、使用接口

複製代碼
    public class DemoOne
    {
        [Import]
        DemoOneInterface DO;

        public void Run()
        {
            DO.Send("DemoOne.Run");
        }
    }
複製代碼
使用[Import]標記需要導入屬性(DemoOneInterface DO;),如果不標記,則MEF不會進行導入。

c、創建插件類

複製代碼
    [Export(typeof(DemoOneInterface))]
    public class DemoOneInherit1 : DemoOneInterface
    {

        #region DemoOneInterface Members

        public void Send(string msg)
        {
            Console.WriteLine("DemoOneInherit1 send {0}", msg);
        }

        #endregion
    }
複製代碼

 插件類需要使用Export標記,並且聲稱導出類型。 

d、查看效果

  static void Main(string[] args)
        {
            new DemoOne().Run();

            Console.ReadLine();
        }

 

原來我們使用MEF,但並沒有通知MEF去尋找插件。

我們對Main函數進行修改:

複製代碼
var demo = new DemoOne();

            var catalog = new AggregateCatalog();

            catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly));

            //catalog.Catalogs.Add(new DirectoryCatalog("Addin"));   //遍歷運行目錄下的Addin文件夾,查找所需的插件。

            var _container = new CompositionContainer(catalog);

            _container.ComposeParts(demo);
            
            demo.Run();
複製代碼

 

修改後再次運行看看效果。

OK,運行起來了,和預期一樣。

 

2、例子二

運行例子一,沒有問題,但2個插件使用同一個的時候,會報錯。

因此我們可以爲Export加入別名(contractName),並且Import的時候也指定別名,MEF就會根據別名自動進行加載。

修改後代碼如下:

複製代碼
public class DemoOne
    {
        [Import("2")]
        DemoOneInterface DO;

        public void Run()
        {
            DO.Send("DemoOne.Run");
        }
    }

    public interface DemoOneInterface   
    {
        void Send(string msg);
    }

    [Export("1",typeof(DemoOneInterface))]
    public class DemoOneInherit1 : DemoOneInterface
    {

        #region DemoOneInterface Members

        public void Send(string msg)
        {
            Console.WriteLine("DemoOneInherit1 send {0}", msg);
        }

        #endregion
    }



    [Export("2", typeof(DemoOneInterface))]
    public class DemoOneInherit12 : DemoOneInterface
    {

        #region DemoOneInterface Members

        public void Send(string msg)
        {
            Console.WriteLine("DemoOneInherit2 send {0}", msg);
        }

        #endregion
    }
複製代碼

 

 運行效果:

3、例子三

有時我們希望一個同時使用多個插件,比如:輸出log。

這時我們可以將Import改爲ImportMany,並且修改Do的類型爲IEnumerable<DemoOneInterface>來導入多個插件。

修改後代碼:

複製代碼
   public class DemoOne
    {
        [ImportMany]
        IEnumerable<DemoOneInterface> DoList;

        public void Run()
        {
            foreach (var _do in DoList)
            {
                _do.Send("DemoOne.Run");
            }
        }
    }

    public interface DemoOneInterface   
    {
        void Send(string msg);
    }

    [Export(typeof(DemoOneInterface))]
    public class DemoOneInherit1 : DemoOneInterface
    {

        #region DemoOneInterface Members

        public void Send(string msg)
        {
            Console.WriteLine("DemoOneInherit1 send {0}", msg);
        }

        #endregion
    }



    [Export(typeof(DemoOneInterface))]
    public class DemoOneInherit12 : DemoOneInterface
    {

        #region DemoOneInterface Members

        public void Send(string msg)
        {
            Console.WriteLine("DemoOneInherit2 send {0}", msg);
        }

        #endregion
    }
複製代碼

 

運行效果:

4、例子四

現在有很多插件使用同一個約定,但我想根據配置在同一個方法中調用某個插件。

這時我們需要使用ExportMetadata來爲插件的特殊屬性進行標記。

使用到Lazy,來進行延遲加載,並且獲取插件標記的信息。(關於Lazy具體信息請自行查找)

a、新增插件描述類

    public interface DemoOneInterfaceDepict
    {
        string Depict{get;}
    }

 

b、爲插件定義描述

複製代碼
 [Export(typeof(DemoOneInterface))]
    [ExportMetadata("Depict", "1")]
    public class DemoOneInherit1 : DemoOneInterface
    {

        #region DemoOneInterface Members

        public void Send(string msg)
        {
            Console.WriteLine("DemoOneInherit1 send {0}", msg);
        }

        #endregion
    }



    [Export(typeof(DemoOneInterface))]
    [ExportMetadata("Depict", "2")]
    public class DemoOneInherit12 : DemoOneInterface
    {

        #region DemoOneInterface Members

        public void Send(string msg)
        {
            Console.WriteLine("DemoOneInherit2 send {0}", msg);
        }

        #endregion
    }
複製代碼

 

c、修改DoList

IEnumerable<Lazy<DemoOneInterface,DemoOneInterfaceDepict>> DoList;

d、根據配置調用 

複製代碼
 public class DemoOne
    {
        [ImportMany]
        IEnumerable<Lazy<DemoOneInterface,DemoOneInterfaceDepict>> DoList;

        public void Run()
        {
            foreach (var _do in DoList.Where(item=>item.Metadata.Depict == ReadXml()))
            {
                _do.Value.Send("DemoOne.Run");
            }
        }

        string ReadXml()
        {
            return "2";
        }
    }
複製代碼

 

運行結果:

 

三、簡化調用

上述4個例子運行正常,但我們一直沒去在意Main函數裏面的內容。

複製代碼
 var demo = new DemoOne();

            var catalog = new AggregateCatalog();

            catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly));

            //catalog.Catalogs.Add(new DirectoryCatalog("Addin"));   //遍歷運行目錄下的Addin文件夾,查找所需的插件。

            var _container = new CompositionContainer(catalog);

            _container.ComposeParts(demo);
            
            demo.Run();
複製代碼

 

看着頭就暈了,難道每次構造一個函數,都這麼寫嗎?那不是非常痛苦?!!!

重新設計一下:

1、使用基類

複製代碼
    public abstract class BaseClass
    {
        public BaseClass()
        {
            var catalog = new AggregateCatalog();

            catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly));

            var _container = new CompositionContainer(catalog);

            _container.ComposeParts(this);
        }
    }
複製代碼

 

修改DemoOne類繼承BaseClass

 public class DemoOne : BaseClass

 

簡化調用

 var demo = new DemoOne();
 demo.Run();

 

運行 ok。

 

2、使用擴展方法

每個類都要繼承這個基類,由於C#只有單繼承,已經繼承了一個基類後,就比較麻煩。

因此衍生出第二種方法,新增擴展方法。

擴展方法

複製代碼
public static class ObjectExt
    {
        public static T ComposePartsSelf<T>(this T obj) where T : class
        {
            var catalog = new AggregateCatalog();

            catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly));
            catalog.Catalogs.Add(new DirectoryCatalog("."));
            //catalog.Catalogs.Add(new DirectoryCatalog("addin"));

            var _container = new CompositionContainer(catalog);

            _container.ComposeParts(obj);

            return obj;
        }
    }
複製代碼

 

修改DemoOne類,新增構造函數,並且調用擴展方法

複製代碼
  public class DemoOne 
    {
        public DemoOne()
        {
            this.ComposePartsSelf();
        }

        [ImportMany]
        IEnumerable<Lazy<DemoOneInterface,DemoOneInterfaceDepict>> DoList;

        public void Run()
        {
            foreach (var _do in DoList.Where(item=>item.Metadata.Depict == ReadXml()))
            {
                _do.Value.Send("DemoOne.Run");
            }
        }

        string ReadXml()
        {
            return "2";
        }
    }
複製代碼

 

簡化調用

 var demo = new DemoOne();
 demo.Run();

運行 ok。

作者:Cosmokey 來自:http://www.cnblogs.com/comsokey 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章