您好,登錄后才能下訂單哦!
閱讀目錄:
1.開篇介紹
2.程序書簽(代碼書簽機(jī)制)
2.1ProgramBookmark 實(shí)現(xiàn)(使用委托來(lái)錨點(diǎn)代碼書簽)
2.2ProgramBookmarkManager書簽管理器(對(duì)象化書簽集合的處理,IEnumerable<T>書簽管理)
3.可恢復(fù)語(yǔ)句組件(將語(yǔ)句對(duì)象化)
3.1可恢復(fù)語(yǔ)句組件管理器(將可恢復(fù)語(yǔ)句視為普通的對(duì)象成員,IEnumerable<T>可恢復(fù)語(yǔ)句組件)
3.2可恢復(fù)語(yǔ)句組件運(yùn)行時(shí)(Program CLR(簡(jiǎn)介))
3.3可恢復(fù)語(yǔ)句邏輯配置(規(guī)則的配置(簡(jiǎn)介))
3.4可恢復(fù)語(yǔ)句邏輯傳輸(將邏輯語(yǔ)句對(duì)象遠(yuǎn)程傳輸(簡(jiǎn)介))
4.DomainModel規(guī)則引擎(規(guī)則持久化后管理配置(簡(jiǎn)介))
這一篇文章我早準(zhǔn)備寫的,遲遲未寫的原因是它過于抽象不太容易表達(dá),也很難掌握;之前對(duì)它的理解還處于比較簡(jiǎn)單的功能性上,但是最近隨著對(duì)領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)及架構(gòu)的研究,設(shè)計(jì)思想有了一個(gè)提升對(duì)它的理解也有了一個(gè)更清晰的輪廓,所以才敢下手去寫,這么好的一篇文章不能搞砸了;
“鈍化語(yǔ)句” 簡(jiǎn)單描述:將基于棧的調(diào)用抽象成基于我們自己構(gòu)建的虛擬運(yùn)行時(shí)調(diào)用;
比如我們可以將普通的IF\ELSE調(diào)用進(jìn)行對(duì)象化,然后就可以對(duì)他們進(jìn)行面向?qū)ο蟮脑O(shè)計(jì)了;能做的事情就太多了,比如將所有的方法放入一個(gè)for循環(huán)語(yǔ)句組件當(dāng)中去,它會(huì)自動(dòng)的去循環(huán)執(zhí)行,而不需要我們?cè)偃プ约簩慺or語(yǔ)句;然后在此基礎(chǔ)上進(jìn)行代碼書簽抽象對(duì)所有的代碼片段進(jìn)行類似邏輯錨點(diǎn)的設(shè)定;
更嚇人的是可以瞬間將語(yǔ)句組件鈍化,其實(shí)也就是瞬間凍結(jié)然后持久化,在遙遠(yuǎn)的地方再將它喚醒執(zhí)行,很可能你的語(yǔ)句在你這臺(tái)電腦上執(zhí)行了一半由于你臨時(shí)有事然后語(yǔ)句被鈍化,在另外一臺(tái)電腦上繼續(xù)你的工作,是不是很方便;當(dāng)然它的使用方式多種多樣了;
我相信這篇文章絕對(duì)讓你對(duì) .NET框架設(shè)計(jì) 感興趣,框架設(shè)計(jì)思想其實(shí)真的很美,讓人陶醉;
美好的一切都要有一個(gè)良性的開始,程序的鈍化少不了對(duì)程序的邏輯保存的功能;有一個(gè)連續(xù)的調(diào)用穿過N個(gè)方法,方法一調(diào)用方法二,方法二調(diào)用方法三,這樣的調(diào)用層次是根據(jù)業(yè)務(wù)的需求來(lái)定的,就好比一個(gè)復(fù)雜的業(yè)務(wù)邏輯這樣的處理下去合情合理;
那么什么是代碼書簽?zāi)??其?shí)我們仔細(xì)分析一下我們?nèi)粘K鶎懙拇a基本上都是由方法組合而成,不管是實(shí)例類還是靜態(tài)類都是通過方法將彼此聯(lián)系起來(lái),所有的業(yè)務(wù)邏輯都是包裝在方法的內(nèi)部處理的,這里的代碼書簽就是方法的可持久化抽象;
試想一下,我們要想將程序的邏輯流程鈍化肯定是少不了對(duì)邏輯調(diào)用的保存;原本的程序邏輯是線程本地的執(zhí)行路徑,屬于.NETCLR直接管理的,依賴于棧的執(zhí)行,所以我們無(wú)法干預(yù)其生命周期過程,那么我們只有將它們對(duì)象化后才能由我們自己操控;
圖1:
上圖的意思是說(shuō)在一個(gè)流程的開始到結(jié)束基本上三個(gè)重要環(huán)節(jié),Begin\Processs…\End過程,在每個(gè)過程中需要不同的處理邏輯,在圖的偏上方,我們有三個(gè)ProcessName名稱的小方塊表示程序的調(diào)用順序,ProcessName1調(diào)用ProcessName2調(diào)用ProcessName3;
在ProcessName2的上面我們加了一個(gè)Bookmark的標(biāo)記,表示我們這里所說(shuō)的代碼書簽,通過代碼書簽我們就可以記錄下本次執(zhí)行到哪里了,就好比我們?cè)诳磿臅r(shí)候都有一個(gè)買書時(shí)贈(zèng)送的書簽卡,我們看到哪里就把這個(gè)書簽卡插在那里,當(dāng)下次要看的時(shí)候直接找到這個(gè)書簽卡繼續(xù)看;
這里的代碼書簽跟這個(gè)是一樣的道理,理論就是這些我們下面通過示例代碼來(lái)親身體驗(yàn)一下這種設(shè)計(jì)模式;
委托是天生的方法標(biāo)簽,通過委托我們完全可以將一個(gè)實(shí)例的方法直接錨定下來(lái);
【有關(guān)對(duì)委托的高級(jí)應(yīng)用不太清楚的可以參見本人的這兩篇文章:
.NET框架設(shè)計(jì)(一:常被忽視的C#設(shè)計(jì)技巧)、.NET框架設(shè)計(jì)(二:常被忽視的框架設(shè)計(jì)技巧)】
我們來(lái)構(gòu)造代碼書簽對(duì)象:
/*============================================================================== * Author:深度訓(xùn)練 * Create time: 2013-08-10 * Blog Address:http://www.cnblogs.com/wangiqngpei557/ * Author Description:特定領(lǐng)域軟件工程實(shí)踐; *==============================================================================*/ namespace ProgramComponent { using System; /// <summary> /// Program book mark. /// </summary> [Serializable] public class ProgramBookmark { /// <summary> /// Mark program book mark. /// </summary> /// <param name="name">Mark name.</param> /// <param name="continueAt">Program continue.</param> public ProgramBookmark(string name, ProgramBookmarkLocation continueAt) { this.markname = name; this.continueAt = continueAt; } private string markname; /// <summary> /// Book mark name. /// </summary> public string BookmarkName { get { return markname; } } private ProgramBookmarkLocation continueAt; /// <summary> /// Continue location. /// </summary> public ProgramBookmarkLocation ContinueAt { get { return continueAt; } } /// <summary> /// Program load data. /// </summary> public object Payload { get; set; } } /// <summary> /// Program book mark location. /// </summary> /// <param name="resumed">Resumed bookmark.</param> public delegate void ProgramBookmarkLocation(ProgramBookmark resumed); }
這段代碼是對(duì)代碼書簽的抽象,構(gòu)造函數(shù)傳入一個(gè)代碼書簽的名稱、書簽所表示的物理代碼錨點(diǎn),Payload是表示每次執(zhí)行物理代碼時(shí)的輸入?yún)?shù);
上面代碼看似簡(jiǎn)單其實(shí)很不簡(jiǎn)單,它的背后隱藏著一個(gè)很大的設(shè)計(jì)思想:
將一塊很大的邏輯代碼拆成很多零碎的方法片段,很多人可能會(huì)覺得設(shè)計(jì)本身不就這樣要求的嘛,那你可能真的沒有深入理解代碼碎片會(huì)后需要對(duì)所有的方法參數(shù)進(jìn)行對(duì)象化,不管什么方法都會(huì)是同樣的參數(shù),只有這樣才能讓書簽連續(xù)起作用;
下面我們來(lái)看一下代碼書簽有多巧妙,我們來(lái)構(gòu)造一個(gè)簡(jiǎn)單的示例代碼,當(dāng)然你完全可以設(shè)計(jì)的很復(fù)雜很強(qiáng)大,這里畢竟是傳遞這種設(shè)計(jì)思想為主;
/*============================================================================== * Author:深度訓(xùn)練 * Create time: 2013-08-10 * Blog Address:http://www.cnblogs.com/wangiqngpei557/ * Author Description:特定領(lǐng)域軟件工程實(shí)踐; *==============================================================================*/ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleApplication1 { [Serializable] public class OrderCheckFlows { private IList<ProgramComponent.ProgramBookmark> flowsManager = new List<ProgramComponent.ProgramBookmark>(); public OrderCheckFlows() { ProgramComponent.ProgramBookmark bookmarkCheckOrderPrices = new ProgramComponent.ProgramBookmark("checkPrices", new ProgramComponent.ProgramBookmarkLocation(CheckOrderPrices)); flowsManager.Add(bookmarkCheckOrderPrices); } public void StartCheck() { do { flowsManager[0].ContinueAt(flowsManager[0]); } while (flowsManager.Count > 0); } #region business flows public void CheckOrderPrices(ProgramComponent.ProgramBookmark nextCheck) { Console.WriteLine("checkPrices..."); ProgramComponent.ProgramBookmark bookmarkCheckOrderPrices = new ProgramComponent.ProgramBookmark("checkPrices", new ProgramComponent.ProgramBookmarkLocation(CheckOrderItems)); bookmarkCheckOrderPrices.Payload = true;//method parameters. flowsManager.Add(bookmarkCheckOrderPrices); flowsManager.RemoveAt(0); } public void CheckOrderItems(ProgramComponent.ProgramBookmark nextCheck) { if ((bool)nextCheck.Payload) { Console.WriteLine("checkItems..."); } else { Console.WriteLine("end check items."); } flowsManager.RemoveAt(0); } #endregion } }
這個(gè)類是一個(gè)簡(jiǎn)單的模擬檢查訂單的一系列的業(yè)務(wù)流程;
圖2:
上圖能看見流程順利執(zhí)行完畢了,那么我們來(lái)解釋一下重要的代碼片段;
圖3:
在第一個(gè)流程里面我們構(gòu)造一個(gè)通往下一個(gè)流程的 ProgramComponent.ProgramBookmark 對(duì)象,如果這里出現(xiàn)關(guān)于流程無(wú)法繼續(xù)下去的條件就可以不創(chuàng)建往下執(zhí)行的代碼書簽;在第二流程里面我們獲取第一個(gè)流程設(shè)置的參數(shù),這里是一個(gè)Bool值,可以用來(lái)判斷上一個(gè)執(zhí)行是否成功等信息;
上一節(jié)我們完成了對(duì)代碼書簽的抽象實(shí)現(xiàn),但是代碼還有很多值得抽象設(shè)計(jì)的地方,上面的代碼中最不太理解的地方就是對(duì)書簽集合的操作上,很不OO;
那么這一節(jié)我們將把它改進(jìn),形成OO方式的調(diào)用,先看一下哪里不太理解;
圖4:
第一個(gè)地方就是在聲明ProgramCompoent.ProgramBookmark集合上,這樣寫問題太大了,無(wú)法進(jìn)行擴(kuò)展改進(jìn);然后就是在構(gòu)造函數(shù)中,我們使用了很長(zhǎng)一段代碼來(lái)構(gòu)造一個(gè)ProgramCompoent.ProgramBookmark對(duì)象,完全可以減少很多;還有就是在StartCheck方法的內(nèi)部中進(jìn)行循環(huán)調(diào)用書簽的代碼,也很有問題,完全可以封裝在內(nèi)部實(shí)現(xiàn),外部直接一個(gè)CurrentProgram屬性執(zhí)行就行了;
那么對(duì)這些問題我們其實(shí)少一個(gè)ProgramCompoent.ProgramBookmark的管理器對(duì)象ProgramCompoent.ProgramBookmarkManager對(duì)象,它負(fù)責(zé)管理所有跟ProgramCompoent.ProgramBookmark對(duì)象相關(guān)的工作;
/*============================================================================== * Author:深度訓(xùn)練 * Create time: 2013-08-10 * Blog Address:http://www.cnblogs.com/wangiqngpei557/ * Author Description:特定領(lǐng)域軟件工程實(shí)踐; *==============================================================================*/ namespace ProgramComponent { using System.Collections.Generic; /// <summary> /// Program book mark Manager.<see cref="System.Collections.Dictionary{BookmarkName,ProgramBookmark}"/> /// </summary> public class ProgramBookmarkManager : Dictionary<string, ProgramBookmark> { /// <summary> /// Add programbookmark and instant next programbookmark. /// </summary> /// <param name="bookmark"><see cref="ProgramComponent.ProgramBookmark"/></param> public void Add(ProgramBookmark bookmark) { base.Add(bookmark.BookmarkName, bookmark); } /// <summary> /// Remove programbookmark. /// </summary> /// <param name="bookmark"><see cref="ProgramComponent.ProgramBookmark"/></param> public void Remove(ProgramBookmark bookmark) { base.Remove(bookmark.BookmarkName); } /// <summary> /// Resume bookmark by bookmarkname. /// </summary> /// <param name="bookmarkName">bookmark name.</param> /// <param name="payload">Continue load.</param> public void Resume(string bookmarkName, object payload) { ProgramBookmark bookmark; this.TryGetValue(bookmarkName, out bookmark); if (bookmark != null) { bookmark.Payload = payload; bookmark.ContinueAt(bookmark); } } } }
書簽管理器基本功能還算簡(jiǎn)單,主要的方法Resume是用來(lái)恢復(fù)指定的書簽的;再來(lái)看一下訂單檢查流程調(diào)用;
/*============================================================================== * Author:深度訓(xùn)練 * Create time: 2013-08-10 * Blog Address:http://www.cnblogs.com/wangiqngpei557/ * Author Description:特定領(lǐng)域軟件工程實(shí)踐; *==============================================================================*/ namespace ConsoleApplication1 { using System; using ProgramComponent; [Serializable] public class OrderCheckFlows { private ProgramBookmarkManager BookmarkManager = new ProgramBookmarkManager(); public OrderCheckFlows() { BookmarkManager.Add(new ProgramBookmark("checkPrices", new ProgramBookmarkLocation(CheckOrderPrices))); } public void StartCheck() { BookmarkManager.Resume("checkPrices", null); } #region business flows public void CheckOrderPrices(ProgramComponent.ProgramBookmark nextCheck) { Console.WriteLine("checkPrices..."); BookmarkManager.Remove(nextCheck); BookmarkManager.Add(new ProgramBookmark("checkItems", new ProgramBookmarkLocation(CheckOrderItems))); BookmarkManager.Resume("checkItems", true); } public void CheckOrderItems(ProgramComponent.ProgramBookmark nextCheck) { if ((bool)nextCheck.Payload) Console.WriteLine("checkItems..."); else Console.WriteLine("end check items."); BookmarkManager.Remove(nextCheck); } #endregion } }
是不是比之前的代碼好多了,我感覺是好多了,當(dāng)然還有很大的重構(gòu)空間;
這里其實(shí)已經(jīng)可以和鏈?zhǔn)骄幊痰臋C(jī)制掛鉤了,我們可以通過給書簽管理器添加N個(gè)擴(kuò)展方法來(lái)使書簽管理器具有跟鏈?zhǔn)降恼{(diào)用;
要想把所有的調(diào)用都拆開來(lái)使用松散的方式組合,通過使用書簽機(jī)制基本上能將所有的方法進(jìn)行松散組合;那么我們還需要將邏輯語(yǔ)法進(jìn)行對(duì)象化才能做到無(wú)死角的松散;
什么叫語(yǔ)句組件,就是將一些原本無(wú)法獨(dú)立的一些邏輯判斷、循環(huán)之類的語(yǔ)句對(duì)象化,形成更具有對(duì)象的組件;
試想一下,如果我們將所有的這些邏輯語(yǔ)法對(duì)象化后我們的代碼中還有精密耦合的代碼嗎?就算有也應(yīng)該會(huì)很少,是不是很神奇;
其實(shí)對(duì) 企業(yè)應(yīng)用架構(gòu) 中的 規(guī)約模式 有所了解的人應(yīng)該會(huì)比較熟悉這一節(jié)的內(nèi)容,跟規(guī)約模式很像,但不是一個(gè)東西,側(cè)重點(diǎn)不同;語(yǔ)句組件全面的概念是將所有的調(diào)用都對(duì)象化,包括一些輸出、輸入、網(wǎng)絡(luò)調(diào)用等等,這樣才是全部的語(yǔ)句組件定義,還記得我們上面的訂單檢查對(duì)象嘛,那個(gè)也是語(yǔ)句組件之一;
我們來(lái)構(gòu)造可恢復(fù)語(yǔ)句組件對(duì)象;
ProgramComponent.LanguageComponent.LanguageComponent類代碼:
/*============================================================================== * Author:深度訓(xùn)練 * Create time: 2013-08-10 * Blog Address:http://www.cnblogs.com/wangiqngpei557/ * Author Description:特定領(lǐng)域軟件工程實(shí)踐; *==============================================================================*/ namespace ProgramComponent.LanguageComponent { using System; [Serializable] public abstract class LanguageComponent { public abstract void Run(ProgramBookmarkManager mgr); } }
ProgramComponent.LanguageComponent.LanguageComponentBlock類代碼:
/*============================================================================== * Author:深度訓(xùn)練 * Create time: 2013-08-10 * Blog Address:http://www.cnblogs.com/wangiqngpei557/ * Author Description:特定領(lǐng)域軟件工程實(shí)踐; *==============================================================================*/ namespace ProgramComponent.LanguageComponent { using System.Collections.Generic; public class LanguageComponentBlock : LanguageComponent { List<LanguageComponent> statements = new List<LanguageComponent>(); public List<LanguageComponent> Statements { get { return statements; } } public void AddLangugateComponent(LanguageComponent lc) { statements.Add(lc); } public override void Run(ProgramBookmarkManager mgr) { } } }
ProgramComponent.LanguageComponent.IfElseLanguageComponent類代碼:
/*============================================================================== * Author:深度訓(xùn)練 * Create time: 2013-08-10 * Blog Address:http://www.cnblogs.com/wangiqngpei557/ * Author Description:特定領(lǐng)域軟件工程實(shí)踐; *==============================================================================*/ namespace ProgramComponent.LanguageComponent { using System; using System.Linq; using System.Linq.Expressions; public class IfElseLanguageComponent : LanguageComponentBlock { public Func<bool> Exp { get; set; } public override void Run(ProgramBookmarkManager mgr) { if (Exp()) base.Statements[0].Run(mgr); else base.Statements[1].Run(mgr); } } }
檢查流程代碼,OrderCheckFlows\OrderSubmitFlows類代碼:
/*============================================================================== * Author:深度訓(xùn)練 * Create time: 2013-08-10 * Blog Address:http://www.cnblogs.com/wangiqngpei557/ * Author Description:特定領(lǐng)域軟件工程實(shí)踐; *==============================================================================*/ namespace ConsoleApplication1 { using System; using ProgramComponent; using ProgramComponent.LanguageComponent; [Serializable] public class OrderCheckFlows : LanguageComponent { private ProgramBookmarkManager BookmarkManager = new ProgramBookmarkManager(); public OrderCheckFlows(ProgramBookmarkManager bookmarkManager) { this.BookmarkManager = bookmarkManager; BookmarkManager.Add(new ProgramBookmark("checkPrices", new ProgramBookmarkLocation(CheckOrderPrices))); } public override void Run(ProgramBookmarkManager mgr) { this.BookmarkManager = mgr; StartCheck(); } public void StartCheck() { BookmarkManager.Resume("checkPrices", null); } #region business flows public void CheckOrderPrices(ProgramComponent.ProgramBookmark nextCheck) { Console.WriteLine("checkPrices..."); BookmarkManager.Remove(nextCheck); BookmarkManager.Add(new ProgramBookmark("checkItems", new ProgramBookmarkLocation(CheckOrderItems))); BookmarkManager.Resume("checkItems", true); } public void CheckOrderItems(ProgramComponent.ProgramBookmark nextCheck) { if ((bool)nextCheck.Payload) Console.WriteLine("checkItems..."); else Console.WriteLine("end check items."); BookmarkManager.Remove(nextCheck); } #endregion } [Serializable] public class OrderSubmitFlows : LanguageComponent { private ProgramBookmarkManager BookmarkManager = new ProgramBookmarkManager(); public OrderSubmitFlows(ProgramBookmarkManager bookmarkManager) { this.BookmarkManager = bookmarkManager; BookmarkManager.Add(new ProgramBookmark("CheckSubmitPrices", new ProgramBookmarkLocation(CheckSubmitPrices))); } public override void Run(ProgramBookmarkManager mgr) { this.BookmarkManager = mgr; StartCheck(); } public void StartCheck() { BookmarkManager.Resume("CheckSubmitPrices", null); } #region business flows public void CheckSubmitPrices(ProgramComponent.ProgramBookmark nextCheck) { Console.WriteLine("CheckSubmitPrices..."); BookmarkManager.Remove(nextCheck); BookmarkManager.Add(new ProgramBookmark("CheckSubmitItems", new ProgramBookmarkLocation(CheckSubmitItems))); BookmarkManager.Resume("CheckSubmitItems", true); } public void CheckSubmitItems(ProgramComponent.ProgramBookmark nextCheck) { if ((bool)nextCheck.Payload) Console.WriteLine("CheckSubmitItems..."); else Console.WriteLine("end check CheckSubmitItems."); BookmarkManager.Remove(nextCheck); } #endregion } }
調(diào)用代碼:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleApplication1 { using ProgramComponent; using ProgramComponent.LanguageComponent; class Program { static void Main(string[] args) { ProgramBookmarkManager bookmarkManager = new ProgramBookmarkManager(); OrderCheckFlows orderCheckFlow = new OrderCheckFlows(bookmarkManager); OrderSubmitFlows submitCheckFlow = new OrderSubmitFlows(bookmarkManager); IfElseLanguageComponent languageComponent = new IfElseLanguageComponent(); languageComponent.Exp = () => { return true; }; languageComponent.AddLangugateComponent(orderCheckFlow); languageComponent.AddLangugateComponent(submitCheckFlow); languageComponent.Run(bookmarkManager); Console.ReadLine(); } } }
一切都已經(jīng)被對(duì)象化,我們來(lái)看一下邏輯;
圖5:
這里的返回值決定了后面要執(zhí)行的語(yǔ)句組件的路徑,如果是true,則應(yīng)該檢查OrderCheckFlows流程;
圖6:
如果是false,則應(yīng)該檢查OrderSubmitFlows流程;
圖7:
可恢復(fù)語(yǔ)句對(duì)象模型基本構(gòu)造完成,當(dāng)然復(fù)雜的問題還需要仔細(xì)的去分析設(shè)計(jì),這里只是一個(gè)簡(jiǎn)單的示例;
跟代碼書簽管理器一個(gè)道理,這里我們也可以實(shí)現(xiàn)一個(gè)LanguageComponentManager來(lái)對(duì)LanguageComponent管理,當(dāng)然也要看需要不需要;可恢復(fù)語(yǔ)句管理器其實(shí)有很多文章可以做,因?yàn)樗撬姓Z(yǔ)句組件的中心,這對(duì)于后面的持久化有很大的用處;
//由于內(nèi)容比較多且相當(dāng)抽象,下一篇文章介紹;
所有的語(yǔ)句代碼都已經(jīng)被對(duì)象化,但是在運(yùn)行時(shí)需要一個(gè)中心來(lái)管理這些被對(duì)象化的語(yǔ)句組件,因?yàn)槲覀円撾x對(duì)棧的依賴;一組語(yǔ)句組件是單個(gè)示例流程的一部分,但是我們可能會(huì)存在很多一起并行運(yùn)行的流程,所以這是必須要提供的運(yùn)行時(shí);
//由于內(nèi)容比較多且相當(dāng)抽象,下一篇文章介紹;
領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)在使用規(guī)約模式的時(shí)候會(huì)存在動(dòng)態(tài)配置的需求,可以參見這里的語(yǔ)句組件模型,讓規(guī)約最大化的提供配置;
//由于內(nèi)容比較多且相當(dāng)抽象,下一篇文章介紹;
//由于內(nèi)容比較多且相當(dāng)抽象,下一篇文章介紹;
//由于內(nèi)容比較多且相當(dāng)抽象,下一篇文章介紹;
示例Demo地址:http://files.cnblogs.com/wangiqngpei557/ConsoleApplication3.zip
作者:王清培
出處:http://wangqingpei557.blog.51cto.com/
本文版權(quán)歸作者和51CTO共有,歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁(yè)面明顯位置給出原文連接,否則保留追究法律責(zé)任的權(quán)利。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。