国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計 > 正文

Unity 入門

2019-11-17 02:43:52
字體:
供稿:網(wǎng)友

Unity 入門 - 延遲解析

本文大綱:

  • 小引
  • 共享的范例代碼
  • 使用 Lazy<T>
  • 使用自動工廠
  • 注入自定義工廠

小引

當(dāng)我們說「解析某個型別/組件」時,意思通常是呼叫某類別的建構(gòu)函式,以建立其實例(instance)。但有些場合,我們會希望解析時先不要生成對象,而是等到真正要呼叫對象的方法時才建立對象。這種延后建立對象的解析方式,叫做「延遲解析」(deferred resolution)。

延遲解析通常用在哪里呢?一個典型的場合是欲解析的對象的創(chuàng)建過程需要花較多時間(例如解析時可能因為建構(gòu)函式需要注入其他對象,而產(chǎn)生多層巢狀解析的情形),而我們希望能加快這個過程,以提升應(yīng)用程序的響應(yīng)速度。

本文介紹兩種實現(xiàn)延遲解析的作法,一種是 Lazy&lt;T&gt;,另一種是「自動工廠」(automatic factories)。

共享的范例代碼

為了避免往后重復(fù)太多相同的程序代碼,這里先列出共享的接口與類別。假設(shè)情境假設(shè)應(yīng)用程序需要提供訊息通知機制,而此機制需支持多種發(fā)送管道,例如:電子郵件、簡訊服務(wù)(Short Message Service)、行動應(yīng)用程序的訊息推送(push notification)等等。簡單起見,這里僅實作其中兩種服務(wù),而且發(fā)送訊息的部分都使用簡單的 Console.WriteLine()來輸出訊息,方便觀察程序的執(zhí)行結(jié)果。設(shè)計用一個 NotificationManager 類來作為整個訊息通知功能的管理員。各類訊息通知機制則由以下兩個類提供:

  • EmailService:透過電子郵件發(fā)送訊息
  • SmsService:透過簡訊服務(wù)發(fā)送訊息

以上三個類均實作同一個接口: IMessageService, 而且 NotificationManager 只知道 IMessageService 接口,而不直接依賴具象類。下圖描繪了它們的關(guān)系:

代碼

訊息通知管理員的相關(guān)代碼:

public interface INotificationManager{    void Notify(string to, string msg);} public class NotificationManager : INotificationManager{    PRivate readonly IMessageService _msgService = null;     // 從建構(gòu)函式注入訊息服務(wù)對象。    public NotificationManager(IMessageService svc)    {        _msgService = svc;    }        // 利用訊息服務(wù)來發(fā)送訊息給指定對象。    public void Notify(string to, string msg)    {        _msgService.SendMessage(to, msg);    }}

這里采用了 Constructor Injection 的注入方式,由建構(gòu)函式傳入訊息服務(wù)。其中的 Notify 方法則利用事先注入的訊息服務(wù)來發(fā)送訊息給指定的接收對象(自變量 "to")。在真實世界中,你可能會需要用額外的類別來代表訊息接收對象(例如設(shè)計一個 MessageRecipient 類 別來封裝收件人的各項信息),這里為了示范而對這部分做了相當(dāng)程度的簡化。

底下是各類訊息服務(wù)的程序代碼:

public interface IMessageService{    void SendMessage(string to, string msg);} public class EmailService : IMessageService{    public void SendMessage(string to, string msg)    {        Console.WriteLine(" 透過 EmailService 發(fā)送郵件給 {0}。", to);    }} public class SmsService : IMessageService{    public void SendMessage(string to, string msg)    {        Console.WriteLine(" 透過 SmsService 發(fā)送簡訊給 {0}。", to);    }}

使用 Lazy<T>

Unity 可以讓我們直接使用 .NET Framework 內(nèi)建的 Lazy&lt;T&gt; 來實現(xiàn)延遲解析。試比較底下兩個范例,首先是一般的寫法:

// 一般寫法var container = new UnityContainer();container.RegisterType<IMessageService, EmailService>(); // 注冊var svc = container.Resolve<IMessageService>(); // 解析組件(呼叫實作類別的建構(gòu)函式)svc.SendMessage("Michael", "Hello!"); // 使用組件

然后是 Lazy&lt;T&gt; 的延遲解析寫法,由于注冊型別的程序代碼不變,故只列出解析的部分:

var lazyObj = container.Resolve<Lazy<IMessageService>>(); // 延遲解析var svc = lazyObj.Value; // 此時才真正呼叫類別的建構(gòu)函式svc.SendMessage("Michael", "Hello!"); // 使用組件

注:有關(guān) Lazy&T& 的用法,請參閱 MSDN 在線文件,或搜尋關(guān)鍵詞「Lazy of T」。

使用自動工廠

「自動工廠」(automatic factories)指的是 DI 容器能夠自動生成一個輕量級的工廠類別,這樣我們就不用花工夫自己寫了。那么,什么情況會用到自動工廠呢?通常是用來實現(xiàn)「延遲解析」,或者應(yīng)付更復(fù)雜、更動態(tài)的晚期綁定(late binding)的需求。

仍舊以先前提過的 NotificationManager 和 IMessageService 為例。假設(shè) EmailService 這個組件在建立實例時需要花費較長的時間(約五秒),參考以下程序片段:

public class EmailService : IMessageService{    public EmailService()    {        // 以暫停線程的方式來仿真對象生成的過程需要花費較長時間。        System.Threading.Thread.Sleep(5000);    }     // 其余程序代碼在這里并不重要,予以省略。}

如果照一般的組件解析方式,會這么寫:

// (1) 注冊var container = new UnityContainer();container.RegisterType<IMessageService, EmailService>();             // (2) 解析var notySvc = container.Resolve<NotificationManager>(); // (3) 呼叫notySvc.Notify("Michael", "自動工廠范例");

這種寫法,在其中的「(2) 解析」這個步驟會發(fā)生下列動作:

  1. DI 容器欲解析 NotificationManager,發(fā)現(xiàn)其建構(gòu)函式需要傳入 IMessageService 對象,于是先解析 IMessageService。
  2. 由于先前向容器注冊組件時已經(jīng)指定由 EmailService 來作為 IMessageService 的實作類別,故容器會先建立一個 EmailService 對象,然后將此對象傳入 NotificationManager 的建構(gòu)函式,以便建立一個 NotificationManager 對象。

也就是說,在解析 NotificationManager 時便一并建立了 EmailService 對象,故此步驟至少要花五秒的時間才能完成。然而,現(xiàn)在我們想要延后相依對象的創(chuàng)建時機,亦即等到真正呼叫組件的方法時,才真正建立其相依對象的實例。像這種場合,我們可以利用 Func&lt;T&gt; 與 Unity 的「自動工廠」來達到延遲解析相依對象的效果。作法很簡單,只要修改 NotificationManager 類別就行了。如下所示:

class NotificationManager {    private IMessageService _msgService;     private Func<IMessageService> _msgServiceFactory     public NotificationManager(Func<IMessageService> svcFactory)     {        // 把工廠方法保存在委派對象里        _msgServiceFactory = svcFactory;    }     public void Notify(string to, string msg)     {        // 由于每次呼叫 _msgServiceFactory() 時都會建立一個新的 IMessageService 對象,        // 這里用一個私有成員變量來保存先前建立的對象,以免不斷建立新的實例。        // 當(dāng)然這并非必要;有些場合,你可能會想要每次都建立新的相依對象。        if (_msgService == null)        {            _msgService = _msgServiceFactory();         }         _msgService.SendMessage(to, msg);    }}

另一方面,原先的「注冊、解析、呼叫」三步驟的程序代碼都不用任何改變。方便閱讀起見,這里再將注冊組件的程序代碼貼上來:

// (1) 注冊var container = new UnityContainer();container.RegisterType<IMessageService, EmailService>();

請注意,NotificationManager 的建構(gòu)函式要求注入的明明是 Func&lt;IMessageService&gt;,可是向容器注冊組件時,卻依舊寫 IMessageService,而不用改成 Func&lt;IMessageService&gt;(要五毛,給一塊)。如此一來,當(dāng)你想要為既有程序代碼加入延遲解析(延遲建立相依對象)的能力時,就可以少改一些程序代碼。這是 Unity 容器的「自動工廠」提供的好處。

進一步解釋,當(dāng) Unity 容器欲解析 NotificationManager 時,發(fā)現(xiàn)其建構(gòu)函式需要一個 Func&lt;IMessageService&gt;委派(delegate),于是便自動幫你生成這個對象,并將它注入至 NotificationManager 類別的建構(gòu)函式。由于注入的是委派對象(你可以把它當(dāng)作是個工廠方法),故此時并沒有真正建立 IMessageService 對象,而是等到上層模塊呼叫此組件的 Notify 方法時,才透過呼叫委派方法來建立 IMessageService 對象 。

當(dāng)然,Unity 容器的「自動工廠」可能無法滿足某些需求。比如說,有些相依對象的創(chuàng)建邏輯比較復(fù)雜,需要你撰寫自定義的對象工廠。這個時候,你可能會想要知道如何注入自定義工廠。

注入自定義工廠

當(dāng)你想要讓 Unity 容器在解析特定組件時使用你的自定義工廠來建立所需之相依對象,Unity 框架的 InjectionFactory 類別可以派上用場。延續(xù)上一個小節(jié)的 NotificationManager 范例。現(xiàn)在假設(shè)你寫了一個對象工廠來封裝 IMessageService 的創(chuàng)建邏輯,像這樣:

class MessageServiceFactory{    public IMessageService GetService()    {        bool isEmail = CheckIfEmailIsUsed();        if (isEmail)        {            return new EmailService();        }        else        {            return new SmsService();        }    }}

此對象工廠的 GetService 方法會根據(jù)執(zhí)行時期的某些變量來決定要返回 EmailService 還是 SmsService 的實例。EmailService 與 SmsService 這兩個類別都實作了 IMessageService 接口,它們的程序代碼在這里并不重要,故未列出。如需查看這些類別的程序代碼,可參閱稍早的〈共享的范例程序〉一節(jié)的內(nèi)容。

NotificationManager 的建構(gòu)函式與上一節(jié)的范例相同,仍舊是注入 Func&lt;IMessageService&gt;。如下所示:

class NotificationManager {    private IMessageService _msgService;     private Func<IMessageService> _msgServiceFactory     public NotificationManager(Func<IMessageService> svcFactory)     {        // 把工廠方法保存在委派對象里        _msgServiceFactory = svcFactory;    }     // (已省略其他不重要的程序代碼)}

剩下的工作,就是告訴 Unity 容器:「在需要解析 IMessageService 的時候,請使用我的 MessageServiceFactory 來建立對象。」參考以下程序片段:

var container = new UnityContainer(); // 注冊Func&lt;IMessageService&gt; factoryMethod = new MessageServiceFactory().GetService;container.RegisterType&lt;IMessageService&gt;(new InjectionFactory(c =&gt; factoryMethod())); // 解析container.Resolve&lt;NotificationManager&gt;();

注冊組件的部分需要加以說明,如下:

  • 先建立一個 Func&lt;IMessageService&gt; 的委派對象,讓它指向 MessageServiceFactory 對象的 GetService 方法。
  • 接著呼叫 Unity 容器的 RegisterType 方法,告訴容器:解析 IMessageService 時,請用我提供的自定義工廠的 GetService 方法,而這個工廠方法已經(jīng)包在剛才建立的委派對象(變量 factoryMethod),并透過 Unity 的 InjectionFactory 將此工廠方法再包一層,以便保存于 Unity 容器。

此范例所使用的 RegisterType 是個擴充方法,其原型宣告如下:

public static IUnityContainer RegisterType<T>(this IUnityContainer container,                                               params InjectionMember[] injectionMembers); 

InjectionFactory 類別繼承自 InjectionMember,而此范例所使用的建構(gòu)函式之原型宣告為:

public InjectionFactory(Func<IUnityContainer, object> factoryFunc);

注:如需 InjectionFactory 類別的詳細說明,可參考在線文件。

本文摘自:《 .NET 依賴注入》第 7 章。


發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 临澧县| 石景山区| 嘉峪关市| 台南县| 荣成市| 兴和县| 青川县| 若尔盖县| 汕尾市| 安达市| 江孜县| 宜黄县| 浦北县| 沽源县| 周口市| 卓资县| 延庆县| 疏勒县| 芮城县| 沙湾县| 吕梁市| 辉南县| 莱芜市| 宁陵县| 班玛县| 沅陵县| 靖宇县| 汉中市| 从江县| 安化县| 浙江省| 越西县| 淄博市| 碌曲县| 绥江县| 赤城县| 疏附县| 龙州县| 防城港市| 尚志市| 和顺县|