<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
在MEF的宿主中,當我們通過Import宣告匯入的物件時,組裝(Compose)的時候會建立該物件。例如:
interface ILogger { void Log(string message); } [Export(typeof(ILogger))] class ConsoleLogger : ILogger { public void Log(string message) { Console.WriteLine("logger 1" + message); } } class Host { [Import] ILogger _logger = null; public Host() { var catalog = new AssemblyCatalog(this.GetType().Assembly); var container = new CompositionContainer(catalog); //這兒會建立ConsoleLogger物件 container.ComposeParts(this); _logger.Log("hello world"); } }
有的時候,有些元件的建立開銷比較大,但又不會立即使用。此時,我們希望通過延遲初始化的方式將其延遲到使用的時候建立,從而提高效能(常見的是提高啟動速度)。MEF是支援這一模式的,我們只需要修改一下匯入的宣告形式即可。
[Import] Lazy<ILogger> _logger = null;
這樣,Logger就會延遲到第一次使用的時候建立了。
有的時候,對於同一個服務有多個提供者,我們需要從中選擇一個使用。MEF提供了ImportMany來解決這一需求。
有的時候,對於同一個服務有多個提供者,我們需要從中選擇一個使用。MEF提供了ImportMany來解決這一需求。
[Export(typeof(ILogger))] class ConsoleLogger : ILogger { public void Log(string message) { Console.WriteLine(message); } } [Export(typeof(ILogger))] class DbLogger : ILogger { public void Log(string message) { Console.WriteLine(message); } } class Host { [ImportMany] ILogger[] _logger = null; public Host() { var catalog = new AssemblyCatalog(this.GetType().Assembly); var container = new CompositionContainer(catalog); container.ComposeParts(this); _logger.FirstOrDefault(i => i is DbLogger).Log("hello world"); } }
此時,如果我們想使用延遲匯入的時候,就會變成如下形式:
class Host { [ImportMany] Lazy<ILogger>[] _loggerServices = null; public Host() { var catalog = new AssemblyCatalog(this.GetType().Assembly); var container = new CompositionContainer(catalog); //這兒會建立ConsoleLogger物件 container.ComposeParts(this); _loggerServices.FirstOrDefault(i => i.Value is DbLogger).Value.Log("hello world"); } }
咋一看並沒有什麼問題,所有的Logger都是延遲建立的。但是仔細分析一下就會發現,要找到DbLogger的時候,必須遍歷所有的_loggerServices,這個遍歷會導致建立Logger。也就是說,使用第一個Logger的時候可能建立所有的Logger。
那麼,如何實現我們只建立所需要的Logger呢? 這個時候就輪到後設資料出場了,MEF中的後設資料可以將一個資料附加到Export的服務物件中一併匯出,從而可以通過元素據找到對應的服務。首先我們看看最終的效果吧:
public interface ILoggerData { string Name { get; } } class Host { [ImportMany] Lazy<ILogger, ILoggerData>[] _logger = null; public Host() { var catalog = new AssemblyCatalog(this.GetType().Assembly); var container = new CompositionContainer(catalog); container.ComposeParts(this); _logger.FirstOrDefault(i => i.Metadata.Name == "DB Logger").Value.Log("hello world"); } }
這裡首先宣告了一個後設資料型別的介面ILoggerData,然後,匯入的物件變成了Lazy<ILogger, ILoggerData>,這個物件有一個屬性為Metadata,它的型別就是剛才宣告的ILoggerData。匯出的ILogger物件是延遲建立的,而後設資料不是延遲建立的。我們可以通過遍歷ILoggerData來找到所需要的Logger物件,從而實現只建立所使用的Logger物件。
現在的問題是:如何在匯出的時候宣告相關的後設資料。MEF提供了兩種方式:
這種方式是在匯出的服務的時候一併通過ExportMetaDataAttribute屬性標記元素據:
[ExportMetadata("Name", "Console Logger")] [Export(typeof(ILogger))] class ConsoleLogger : ILogger { public void Log(string message) { Console.WriteLine(message); } } [ExportMetadata("Name", "DB Logger")] [Export(typeof(ILogger))] class DbLogger : ILogger { public void Log(string message) { Console.WriteLine(message); } }
ExportMetaDataAttribute有兩個引數,Name和Value,Name為屬性名稱,Value為屬性值。Compse的時候,MEF會先建立一個實現了後設資料物件,然後將根據將Value值賦值給Name所對應的屬性名稱。
這麼做雖然比較簡單,但是它有兩個弊端:
這裡我們必須字串來給標誌屬性名稱,它們之間並沒有語法級的一致性檢查,在缺少nameof運運算元的現在,一旦後設資料屬性名稱更改的話是非常容易出錯的。
假如我們增加了一個Priority型別的屬性,
public interface ILoggerData { string Name { get; } int Priority { get; } }
此時,必須對所有的匯出物件都增加一個ExportMetadata標記,寫出如下形式:
[ExportMetadata("Name", "DB Logger")] [ExportMetadata("Priority", 3)]
當屬性更多一點的話相信程式設計師們就會罵娘了。並且一旦某個Export物件漏寫了,改物件就不會被匯入。這個是一個執行時的錯誤,非常不容易排查的。
這種方式可以通過一個Attribute來標記後設資料:
[MetadataAttribute] [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] class LoggerDataAttribute : Attribute, ILoggerData { public string Name { get; private set; } public LoggerDataAttribute(string name) { this.Name = name; } } [LoggerData("Console Logger")] [Export(typeof(ILogger))] class ConsoleLogger : ILogger, ILoggerData { public string Name { get; set; } public void Log(string message) { Console.WriteLine(message); } } [LoggerData("DB Logger")] [Export(typeof(ILogger))] class DbLogger : ILogger, ILoggerData { public string Name { get; set; } public void Log(string message) { Console.WriteLine(message); } }
首先,宣告一個LoggerDataAttribute,這個Attribute必須被MetadataAttribute標記。然後,在Export的物件前加上該LoggerDataAttribute,這樣MEF匯入的時候就會根據該LoggerDataAttribute建立後設資料了。
值得一提的是,這裡的LoggerDataAttribute本身並不需要實現ILoggerData介面,它是一個DuckType的約定,只需要實現後設資料的屬性即可。我這裡實現該介面主要是為了讓編譯器保障後設資料屬性都有被準確實現。
到此這篇關於C#在MEF框架中實現延遲載入部件的文章就介紹到這了。希望對大家的學習有所幫助,也希望大家多多支援it145.com。
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45