上篇文章《在.NET Core 3.0中的WPF中使用IOC圖文教程》中,咱們嘗試在WPF中應用.NET Core
內置的IOC進行編程,在解析MainWindow
的時候我用了GetRequiredService<T>()
方法,當時就在想這個GetRequiredService<T>()
方法跟GetService<T>()
到底有什麼區別呢,因而乎,谷歌了一把,就發現了一篇文章來介紹他們區別的,因而乎嘗試翻譯一把,但願對你們有所幫助。文章最後會給出原文連接,如下就是翻譯內容:html
做者:依樂祝
原文地址:http://www.javashuo.com/article/p-vrbcckrr-kg.htmlgit
本文將介紹Microsoft.Extensions.DependencyInjection
中提供的默認/內置ASP.NET Core DI容器的方法GetService<T>()
和GetRequiredService<T>()
方法。我將描述它們之間的差別以及您應該使用哪一種方法。github
若是服務不存在則
GetService()
返回null
,GetRequiredService()
而是拋出異常。若是您正在使用第三方容器,請儘量使用GetRequiredService
- 若是發生異常,第三方容器可能就會根據異常信息提供相應的診斷信息,以便您能夠找出未註冊預期服務的緣由。編程
ASP.NET Core依賴注入抽象的核心是IServiceProvider
接口。該接口其實是System
命名空間中基類庫的一部分。接口自己很簡單:框架
public interface IServiceProvider { object GetService(Type serviceType); }
一旦您使用DI容器(使用IServiceCollection
)註冊了全部類,幾乎全部DI容器須要作的就是容許您使用GetService()
查找對象的實例。ide
固然,您一般根本不該該直接在代碼中使用IServiceProvider
。相反,您應該使用標準的構造函數注入,並讓框架來承載並在幕後使用IServiceProvider
。函數
直接使用
IServiceProvider
是服務定位器模式的一個示例。這一般被認爲是反模式,由於它隱藏了類的依賴關係。ui
然而,有些時候你沒有選擇的餘地。例如,若是您試圖將服務注入到屬性,或者在配置DI容器時使用「轉發」類型,則須要直接使用IServiceProvider
。this
鑑於咱們再也不使用.NET 1.0,若是你想從IServiceProvider
中檢索服務,你可能使用了通用的泛型GetService<T>()
擴展方法,而不是GetService(Type)
接口方法。可是你可能也注意到了相似的GetRequiredService<T>()
擴展方法 - 問題是,它們之間有什麼區別呢,您應該使用哪一種方法?.net
在咱們研究任何代碼以前,讓咱們先討論一下這些方法的預期行爲。首先,從GetService()
方法的文檔開始:
GetService()
返回一個serviceType
類型的服務對象。若是返回的是一個沒有類型的服務對象serviceType
則返回null
。
與GetRequiredService()
的文檔內容進行對比:
GetRequiredService()
返回一個serviceType
類型的服務對象。若是沒有serviceType
類型的服務,則拋出一個InvalidOperationException
異常。
所以,當請求的實例serviceType
可用時,兩種方法的行爲都相同。不一樣之處在於serviceType
未註冊時的行爲:
GetService
- 若是服務未註冊,則返回null
GetRequiredService
- 若是服務未註冊,則拋出一個Exception
異常。如今咱們已經清楚了,讓咱們看看一些代碼。
在ServiceProviderServiceExtensions
班上Microsoft.Extensions.DependencyInjection.Abstractions庫中同時實現了通用版GetService<T>()
和GetRequiredService<T>()
方法,以下所示:
我已經從本文的代碼中刪除了一些前提條件檢查; 若是你想看到完整的代碼,請在GitHub上查看。
public static class ServiceProviderServiceExtensions { public static T GetService<T>(this IServiceProvider provider) { return (T)provider.GetService(typeof(T)); } public static T GetRequiredService<T>(this IServiceProvider provider) { return (T)provider.GetRequiredService(typeof(T)); } }
這兩種方法實際上都是相同的 - 通用擴展方法委託給非泛型版本的GetService()
和GetRequiredService()
。它們只是一種便利,所以您在本身的代碼中不須要使用更多的typeof()
和類型轉換。
非泛型版本的GetService()
是IServiceProvider
接口的一部分,但非泛型GetRequiredService()
實現是同一類中的擴展方法:
public static class ServiceProviderServiceExtensions { public static object GetRequiredService(this IServiceProvider provider, Type serviceType) { var requiredServiceSupportingProvider = provider as ISupportRequiredService; if (requiredServiceSupportingProvider != null) { return requiredServiceSupportingProvider.GetRequiredService(serviceType); } var service = provider.GetService(serviceType); if (service == null) { throw new InvalidOperationException(Resources.FormatNoServiceRegistered(serviceType)); } return service; } }
該方法的第一步是檢查提供的IServiceProvider
是否也實現了ISupportRequiredService
。此接口提供底層的非泛型GetRequiredService
實現,所以若是服務提供者實現它,GetRequiredService()
則能夠直接調用。
ASP.NET Core內置的DI容器並無實現
ISupportRequiredService
- 只有第三方容器實現了GetRequiredService()
。
若是IServiceProvider
沒有實現ISupportRequiredService
,則執行所需的異常拋出行爲,如您所料:GetService()
調用,若是返回null
則拋出異常。
正如我以前所說,理想狀況下,二者均可以!
在您本身的代碼使用ISeviceProvider
一般是你正在使用服務定位器反模式的一個標誌,因此通常應避免使用ISeviceProvider
。可是,若是因爲設計限制而須要(例如,您不能在屬性中使用DI),或者做爲DI容器配置自己的一部分的狀況下,您應該使用哪種呢?
基於GitHub中要求添加GetRequiredService()
的原始問題,以及Jeremy D. Miller先前提出的問題 ,我認爲幾乎全部狀況下的規則是:
使用
GetRequiredService()
GetRequiredService()
會當即拋出異常。若是您使用GetService()
,那麼您須要在調用代碼中檢查是否爲null
,而且一般須要拋出異常。那個空檢查代碼須要在任何地方重複。GetService()
時忘記檢查是否爲null
,那麼稍後您的程序可能會以NullReferenceException
結束。找出致使異常的緣由老是比顯式的告訴你的InvalidOperationException
更困難,須要作更多的工做。GetRequiredService()
,則第三方容器自己會生成異常,所以能夠提供其餘特定於容器的信息。只返回null
(帶GetService()
)不會給你進一步的詳細的信息。這是引入GetRequiredService()
的主要緣由。固然,我已經看到了一些反對GetRequiredService()`的觀點,但我認爲其中任何一個都不會受到審查:
ISupportRequiredService
),那麼您將沒法經過使用任何其餘診斷獲益GetRequiredService()
。可是,我認爲前兩個優點仍然存在,並使GetRequiredService
值得使用。此外,若是您之後添加第三方容器,您已經在使用最佳實踐了。GetService()
惟一有效的理由。若是您的代碼只有在註冊了給定服務時才能運行,那麼您可能須要使用GetService()
。可是,若是GetService()
返回NULL,我也看到它在使用回退服務時使用。在我看來,這不多是應用程序代碼的好模式。回退的編排應該是DI容器配置的一部分,而不是使用服務的位置。因此,如今你有了 - GetService()
與GetRequiredService()
之間的對比了。在我進一步挖掘它以前,當我選擇一個而不是另外一個時,我有點武斷,但如今我會確保我老是理所固然的使用GetRequiredService()
。
GetService()
是IServiceProvider
上的惟一方法,ISeviceProvider
是ASP.NET核心DI抽象中的中央接口。第三方容器還能夠實現可選接口ISupportRequiredService
,該接口提供GetRequiredService()
方法。當請求的類型serviceType
可用時,這些方法的行爲相同。若是服務不可用(即它沒有註冊),則GetService()
返回null
,而GetRequiredService()
拋出一個InvalidOperationException
。
GetRequiredService()
相對於GetService()
的主要好處是當服務不可用時,它容許第三方容器提供額外的診斷信息。所以,在使用第三方容器時最好使用GetRequiredService()
。就我的而言,我會在任何地方使用它,即便我只使用內置的DI容器。
原英文連接:https://andrewlock.net/the-difference-between-getservice-and-getrquiredservice-in-asp-net-core/