網站首頁 編程語言 正文
在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就會延遲到第一次使用的時候創建了。
元數據MetaData
有的時候,對于同一個服務有多個提供者,我們需要從中選擇一個使用。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標記聲明
這種方式是在導出的服務的時候一并通過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所對應的屬性名稱。
這么做雖然比較簡單,但是它有兩個弊端:
- 1. 屬性名稱不是強類型
這里我們必須字符串來給標志屬性名稱,它們之間并沒有語法級的一致性檢查,在缺少nameof運算符的現在,一旦元數據屬性名稱更改的話是非常容易出錯的。
- 2. 如果元數據有多個值的話賦值顯得非常累贅。
假如我們增加了一個Priority類型的屬性,
public interface ILoggerData
{
string Name { get; }
int Priority { get; }
}
此時,必須對所有的導出對象都增加一個ExportMetadata標記,寫出如下形式:
[ExportMetadata("Name", "DB Logger")]
[ExportMetadata("Priority", 3)]
當屬性更多一點的話相信程序員們就會罵娘了。并且一旦某個Export對象漏寫了,改對象就不會被導入。這個是一個運行時的錯誤,非常不容易排查的。
通過Attribute標記
這種方式可以通過一個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的約定,只需要實現元數據的屬性即可。我這里實現該接口主要是為了讓編譯器保障元數據屬性都有被準確實現。
原文鏈接:https://www.cnblogs.com/TianFang/p/4069978.html
相關推薦
- 2022-05-21 python?判斷文件或文件夾是否存在_python
- 2022-10-10 C++超詳細分析type_traits_C 語言
- 2022-06-09 FreeRTOS實時操作系統的多優先級實現_操作系統
- 2022-09-22 matplotlib自定義風格
- 2022-12-29 淺析Python是如何實現集合的_python
- 2022-04-18 pytorch自定義loss損失函數_python
- 2022-07-14 python?numpy.ndarray中如何將數據轉為int型_python
- 2022-11-06 Swift使用SnapKit模仿Kingfisher第三方擴展優化_Swift
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支