有些對象我們並不想一開始就實例化,由於性能或者功能的考慮,希望等到使用的時候再實例化。
考慮存在一個類 A, 它使用了依賴的類 B,在 A 中,只有某些不常用到的方法會涉及調用 B 中的方法,多數情況下,並不使用這個 B 的實例。
using System; public class A { private B _b; public A (B b) { _b = b; Console.WriteLine("construct class A..."); } public void MethodOne () { _b.ClassBMethod (); } public void MethodTwo () { // does not use _b } public void MethodThree () { // does not use _b } } public class B { public B (){ Console.WriteLine("construct class b......"); } public void ClassBMethod () { //do something } } }
把它們注冊到容器中,然后使用一下。
using System; using Microsoft.Extensions.DependencyInjection; class Program { static void Main (string[] args) { IServiceCollection services = new ServiceCollection (); services.AddSingleton<B>(); services.AddSingleton<A>(); var provider = services.BuildServiceProvider(); var a = provider.GetService<A>(); a.MethodTwo(); a.MethodThree(); } }
這里僅僅調用了類 A 的 MethodTwo() 和 MethodThree() 這兩個方法,那么,對於類 B 的實例化就是沒有必要的。但是,從輸出中可以看到,由於類 A 依賴了類 B,所以,B 被提前實例化了。
construct class b......
construct class A...
在這種情況下,如何可以避免對類 B 的實例化呢?考慮使用 Lazy<T> 。
當通過 Lazy<T> 的方式注入依賴的類型的時候,我們將延遲了對所依賴對象的構造,而且,Lazy<T> 會被自動注入,與使用注冊在容器中的 T 一樣。
Layz<T> 表示在第一次訪問的時候才會初始化的值。上面的代碼可以修改為如下形式:
using System; public class A { private Lazy<B> _b; public A (Lazy<B> b) { _b = b; Console.WriteLine("construct class A..."); } public void MethodOne () { _b.Value.ClassBMethod (); } public void MethodTwo () { // does not use _b } public void MethodThree () { // does not use _b } } public class B { public B (){ Console.WriteLine("construct class b......"); } public void ClassBMethod () { //do something } } }
注冊的形式也需要調整一下。
static void Main (string[] args) { IServiceCollection services = new ServiceCollection (); services.AddSingleton<Lazy<B>>(); services.AddSingleton<A>(); var provider = services.BuildServiceProvider(); var a = provider.GetService<A>(); a.MethodTwo(); a.MethodThree(); Console.WriteLine("prepare call MethodOne..."); a.MethodOne(); }
對類型 B 的注冊,變成了注冊 Lazy<B>。它是 B 的一個封裝。
現在重新運行程序,可以看到如下的結果。
construct class A...
prepare call MethodOne...
construct class b......
在調用 MethodTwo() 和 MethodThree() 的時候,類型 B 並沒有實例化,直到實際調用 MethodOne() 的時候,實際訪問了類型 B 的實例,它才會實例化。
