# [C#基礎知識系列]專題十七:深入理解動態類型
**本專題概要:**
* **動態類型介紹**
* **為什么需要動態類型**
* **動態類型的使用**
* **動態類型背后的故事**
* **動態類型的約束**
* **實現動態行為**
* **總結**
**引言:**
終于迎來了我們C# 4中特性了,C# 4主要有兩方面的改善——Com 互操作性的改進和動態類型的引入,然而COM互操作性這里就不詳細介紹的,對于.Net 互操作性我將會在另外一個專題中詳細和大家分享下我所了解到的知識,本專題就和大家分享C# 4中的動態類型,對于動態類型,我剛聽到這個名詞的時候會有這些疑問的——動態類型到底是什么的呢? 知道動態類型大概是個什么的時候,肯定又會有這樣的疑問——C# 4中為什么要引入動態類型的?(肯定引入之后可以完成我們之前不能做的事情了,肯定是有好處的),下面就具體介紹了動態類型有哪些內容的。
**一、動態類型介紹**
提到動態類型當然就要說下靜態類型了,對于什么是靜態類型呢? 大家都知道之前C#一直都是靜態語言(指定的是沒有引入動態類型之前,這里說明下,不是引入了動態類型后C#就是動態語言,只是引入動態類型后,為C#語言增添了動態語言的特性,C#仍然是靜態語言),之所以稱為靜態語言,之前我們寫代碼時,例如 int i =5;這樣的代碼,此時i 我們已經明確知道它的類型為int了,然而這樣的代碼,**變量的類型的確定是在編譯時確定的,對應的,如果類型的確定是在執行時才確定的類型,這樣的類型就是動態類型(C# 4.0中新添加了一個dynamic 關鍵字來定義我們的動態類型)。**面對動態類型,C#編譯器做的工作只是完成檢查語法是否正確,但無法確定所調用的方法或屬性是否正確(之所以會這樣,主要還是因為動態類型是運行時才知道它的具體類型,所以編譯器編譯的時候肯定不知道類型,就沒辦法判斷調用的方法或屬性是不是存在和正確了,所以對于動態類型,將不能使用VS提供的智能提示的功能,這樣寫動態類型代碼時就要求開發人員對于某個動態類型必須準確知道其類型后和所具有的方法和屬性了,不能這些錯誤只能在運行程序的過程拋出異常的方式被程序員所發現。)
補充: 講到dynamic關鍵字,也許大家會想到C# 3中的var關鍵字,這里這里補充說明下dynamic, var區別。var 關鍵字不過是一個指令,它告訴編譯器根據變量的初始化表達式來推斷類型。(記住var并不是類型),而C# 4中引入的dynamic是類型,但是編譯時不屬于CLR類型(指的int,string,bool,double等類型,運行時肯定CLR類型中一種的),它是包含了System.Dynamic.DynamicAttribute特性的System.Object類型,但與object又不一樣,不一樣主要體現在動態類型不會在編譯時時執行顯式轉換,下面給出一段代碼代碼大家就會很容易看出區別了:
```
object obj = 10;
Console.WriteLine(obj.GetType());
// 使用object類型此時需要強制類型轉換,不能編譯器會出現編譯錯誤
obj = (int)obj + 10;
dynamic dynamicnum = 10;
Console.WriteLine(dynamicnum.GetType());
// 對于動態類型而言,編譯時編譯器根本不知道它是什么類型,
// 所以編譯器就判斷不了dynamicnum的類型了,所以下面的代碼不會出現編譯時錯誤
// 因為dynamicnum有可能是int類型,編譯器不知道該變量的具體類型不能憑空推測類型
// 當然也就不能提示我們編譯時錯誤了
dynamicnum = dynamicnum + 10;
```
**二、為什么需要動態類型**
第一部分和大家介紹了什么是動態類型,對于動態類型,總結為一句話為——**運行時確定的類型。**然而大家了解了動態類型到底是什么之后,當然又會出現新的問題了,即動態類型有什么用的呢? C# 為什么好端端的引入動態類型增加程序員的負擔呢? 事實并不是這樣的,下面就介紹了動態類型到底有什么用,它并不是所謂給程序員帶來負擔,一定程度上講是福音
**2.1 使用動態類型可以減少強制類型轉換**
從第一部分的補充也可以看到,使用動態類型不需要類型轉換是因為編譯器根本在編譯時的過程知道什么類型,既然不知道是什么類型,怎么判斷該類型是否能進行什么操作,所以也就不會出現類似“運算符“+”無法應用于“object”和“int”類型的操作數“或者”不存在int類型到某某類型的隱式轉換“的編譯時錯誤了,可能這點用戶,開發人員可能并不覺得多好的,因為動態類型沒有智能提示的功能。 但是動態類型減少了強制類型轉換的代碼之后,可讀性還是會有所增強。(這里又涉及到個人取舍問題的, 如果自己覺得那種方式方便就用那種的,沒必要一定要用動態類型,主要是看那種方式可以讓自己和其他開發人員更好理解)
**2.2 使用動態類型可以使C#靜態語言中調用Python等動態語言**
對于這點,可能朋友有個疑問,為什么要在C#中使用Python這樣的動態語言呢? 對于這個疑問,就和在C#中通過P/Invoke與本地代碼交互,以及與COM互操作的道理一樣,假設我們要實現的功能在C#類庫中沒有,然而在Python中存在時,此時我們就可以直接調用Python中存在的功能了。
**三、動態類型的使用**
前面兩部分和大家介紹動態類型的一些基礎知識的,了解完基礎知識之后,大家肯定很迫不及待地想知道如何使用動態類型的,下面給出兩個例子來演示動態類型的使用的。
3.1 C# 4 通過dynamic關鍵字來實現動態類型
```
dynamic dyn = 5;
Console.WriteLine(dyn.GetType());
dyn = "test string";
Console.WriteLine(dyn.GetType());
dynamic startIndex = 2;
string substring = dyn.Substring(startIndex);
Console.WriteLine(substring);
Console.Read();
```
運行結果為: 
3.2 在C#中調用Python動態語言(要運行下面的代碼,必須下載并安裝IronPython,IronPython 是在 .NET Framework 上實現的第一種動態語言。[http://ironpython.codeplex.com](http://ironpython.codeplex.com)下載 )
```
// 引入動態類型之后
// 可以在C#語言中與動態語言進行交互
// 下面演示在C#中使用動態語言Python
ScriptEngine engine = Python.CreateEngine();
Console.Write("調用Python語言的print函數輸出: ");
// 調用Python語言的print函數來輸出
engine.Execute("print 'Hello world'");
Console.Read();
```
運行結果: 
**四、動態類型背后的故事**
知道了如何在C#中調用動態語言之后,然而為什么C# 為什么可以使用動態類型呢?C#編譯器到底在背后為我們動態類型做了些什么事情的呢? 對于這些問題,答案就是DLR(Dynamic Language Runtime,動態語言運行時),DLR使得C#中可以調用動態語言以及使用dynamic的動態類型。提到DLR時,可能大家會想到.Net Framework中的CLR(公共語言運行時),然而DLR 與CLR到底是什么關系呢?下面就看看.Net 4中的組件結構圖,相信大家看完之后就會明白兩者之間的區別:

從圖中可以看出,DLR是建立在CLR的基礎之上的,其實動態語言運行時**是動態語言和C#編譯器用來動態執行代碼的庫,它不具有JIT編譯,垃圾回收等功能。然而DLR在代碼的執行過程中扮演的是什么樣的角色呢? DLR所扮演的角色就是——DLR通過它的綁定器(binder)和調用點(callsite),元對象來把代碼轉換為表達式樹,然后再把表達式樹編譯為IL代碼,最后由CLR編譯為本地代碼(DLR就是幫助C#編譯器來識別動態類型)。** 這里DLR扮演的角色并不是憑空想象出來的,而且查看它的反編譯代碼來推出來的,下面就具體給出一個例子來說明DLR背后所做的事情。C#源代碼如下:
```
class Program
{
static void Main(string[] args)
{
dynamic text = "test text";
int startIndex = 2;
string substring = text.Substring(startIndex);
Console.Read();
}
}
```
通過Reflector工具查看生成的IL代碼如下:
```
private static void Main(string[] args)
{
object text = "test text";
int startIndex = 2;
if (<Main>o__SiteContainer0.<>p__Site1 == null)
{
// 創建用于將dynamic類型隱式轉換為字符串的調用點
<Main>o__SiteContainer0.<>p__Site1 = CallSite<Func<CallSite, object, string>>.Create(Binder.Convert(CSharpBinderFlags.None, typeof(string), typeof(Program)));
}
if (<Main>o__SiteContainer0.<>p__Site2 == null)
{
// 創建用于調用Substring函數的調用點
<Main>o__SiteContainer0.<>p__Site2 = CallSite<Func<CallSite, object, int, object>>.Create(Binder.InvokeMember(CSharpBinderFlags.None, "Substring", null, typeof(Program), new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null) }));
}
// 調用調用點,首先調用<>p_Site2,即Substring方法,再調用<>P_Site1來將結果進行轉換
string substring = <Main>o__SiteContainer0.<>p__Site1.Target(<Main>o__SiteContainer0.<>p__Site1, <Main>o__SiteContainer0.<>p__Site2.Target(<Main>o__SiteContainer0.<>p__Site2, text, startIndex));
Console.Read();
}
//編譯器生成的內嵌類型為
[CompilerGenerated]
private static class <Main>o__SiteContainer0
{
// Fields
public static CallSite<Func<CallSite, object, string>> <>p__Site1;
public static CallSite<Func<CallSite, object, int, object>> <>p__Site2;
}
```
從IL代碼中可以看出Main方法內包含兩個動態操作,因為編譯器生成的內嵌類型包含兩個調用點(CallSite<T>,CallSite<T>即是System.Runtime.CompilerServices命名空間下的一個類,關于CallSite的具體信息可以查看MSDN中的介紹——[CallSite<T>](http://msdn.microsoft.com/zh-cn/library/dd402854.aspx) )字段,一個是調用Substring方法(即**[<>p__Site2](http://www.aisto.com/roeder/dotnet/Default.aspx?Target=code://動態類型Demo:1.0.0.0/動態類型Demo.Program.<Main>o__SiteContainer0/<>p__Site2:System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite,Object,Int32,Object>>)**),一個是將結果(編譯時時dynamic)動態地轉換為字符串(即**<>p__Site1**),下面給出動態類型的執行過程(注意DLR中有一個緩存的概念): 
**五、動態類型的約束**
相信通過前面幾部分的介紹大家已經對動態類型有了一定的了解的,尤其是第四部分的介紹之后,大家應該對于動態類型的執行過程也有了一個清晰的認識了,然而有些函數時不能通過動態綁定來進行調用的,這里就涉及到類型類型的約束:
**5.1 不能用動態類型作為參數調用擴展方法**
不能用動態類型作為參數來調用擴展方法的原因是——調用點知道編譯器所知道的靜態類型,但是它不知道調用所在的源文件在哪里,以及using指令引入了哪些命名空間,所以在編譯時調用點就找不到哪些擴展方法可以使用,所以就會出現編譯時錯誤。下面給出一個簡單的示例程序:
```
var numbers = Enumerable.Range(10, 10);
dynamic number = 4;
var error = numbers.Take(number); // 編譯時錯誤
// 通過下面的方式來解決這個問題
// 1\. 將動態類型轉換為正確的類型
var right1 = numbers.Take((int)number);
// 2\. 用調用靜態方法的方式來進行調用
var right2 = Enumerable.Take(numbers, number);
```
**5.2 委托與動態類型不能隱式轉換的限制**
如果需要將Lambda表達式,匿名方法轉化為動態類型時,此時編譯器必須知道委托的確切類型,不能不加強制轉化就把他們設置為Delegae或object變量,此時不同string,int類型(因為前面int,string類型可以隱式轉化為動態類型,編譯器此時會把他們設置為object類型。但是匿名方法和Lambda表達式不能隱式轉化為動態類型),如果需要完成這樣的轉換,此時必須強制指定委托的類型,下面是一個演示例子:
```
dynamic lambdarestrict = x => x + 1; // 編譯時錯誤
// 解決方案
dynamic rightlambda =(Func<int,int>)( x=>x+1);
dynamic methodrestrict = Console.WriteLine; // 編譯時錯誤
// 解決方案
dynamic rightmethod =(Action<string>)Console.WriteLine;
```
**5.3 動態類型不能調用構造函數和靜態方法的限制**——即不能對動態類型調用構造函數或靜態方法,因為此時編譯器無法指定具體的類型。
**5.4 類型聲明和泛型類型參數**
不能聲明一個基類為dynamic的類型,也不能將dynamic用于類型參數的約束,或作為類型所實現的接口的一部分,下面看一些具體的例子來加深概念的理解:
```
// 基類不能為dynamic 類型
class DynamicBaseType : dynamic
{
}
// dynamic類型不能為類型參數的約束
class DynamicTypeConstrain<T> where T : dynamic
{
}
// 不能作為所實現接口的一部分
class DynamicInterface : IEnumerable<dynamic>
{
}
```
**六、實現動態的行為**
介紹了這么動態類型,是不是大家都迫不及待地想知道如果讓自己的類型具有動態的行為呢? 然而實現動態行為有三種方式:
* 使用ExpandObject
* 使用DynamicObject
* 實現IDynamicMetaObjectProvider接口.
下面就從最簡單的方式:
6.1 使用ExpandObject來實現動態的行為
View Code
```
using System;
// 引入額外的命名空間
using System.Dynamic;
namespace 自定義動態類型
{
class Program
{
static void Main(string[] args)
{
dynamic expand = new ExpandoObject();
// 動態為expand類型綁定屬性
expand.Name = "Learning Hard";
expand.Age = 24;
// 動態為expand類型綁定方法
expand.Addmethod = (Func<int, int>)(x => x + 1);
// 訪問expand類型的屬性和方法
Console.WriteLine("expand類型的姓名為:"+expand.Name+" 年齡為: "+expand.Age);
Console.WriteLine("調用expand類型的動態綁定的方法:" +expand.Addmethod(5));
Console.Read();
}
}
}
```
運行的結果和預期的一樣,運行結果為: 
6.2 使用DynamicObject來實現動態行為
View Code
```
static void Main(string[] args)
{
dynamic dynamicobj = new DynamicType();
dynamicobj.CallMethod();
dynamicobj.Name = "Learning Hard";
dynamicobj.Age = "24";
Console.Read();
}
class DynamicType : DynamicObject
{
// 重寫方法,
// TryXXX方法表示對對象的動態調用
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
Console.WriteLine(binder.Name +" 方法正在被調用");
result = null;
return true;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
Console.WriteLine(binder.Name + " 屬性被設置," + "設置的值為: " + value);
return true;
}
}
```
運行結果為: 
6.3 實現IDynamicMetaObjectProvider接口來實現動態行為
由于Dynamic類型在運行時來動態創建對象的,所以對該類型的每個成員的訪問都會調用GetMetaObject方法來獲得動態對象,然后通過這個動態對象來進行調用,所以實現IDynamicMetaObjectProvider接口,需要實現一個GetMetaObject方法來返回DynamicMetaObject對象,演示代碼如下:
```
static void Main(string[] args)
{
dynamic dynamicobj2 = new DynamicType2();
dynamicobj2.Call();
Console.Read();
}
public class DynamicType2 : IDynamicMetaObjectProvider
{
public DynamicMetaObject GetMetaObject(Expression parameter)
{
Console.WriteLine("開始獲得元數據......");
return new Metadynamic(parameter,this);
}
}
// 自定義Metadynamic類
public class Metadynamic : DynamicMetaObject
{
internal Metadynamic(Expression expression, DynamicType2 value)
: base(expression, BindingRestrictions.Empty, value)
{
}
// 重寫響應成員調用方法
public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args)
{
// 獲得真正的對象
DynamicType2 target = (DynamicType2)base.Value;
Expression self = Expression.Convert(base.Expression, typeof(DynamicType2));
var restrictions = BindingRestrictions.GetInstanceRestriction(self, target);
// 輸出綁定方法名
Console.WriteLine(binder.Name + " 方法被調用了");
return new DynamicMetaObject(self, restrictions);
}
}
```
運行結果為: 
**七、總結**
講到這里動態類型的介紹就已經介紹完了,本專題差不多涵蓋了動態類型中所有內容,希望通過本專題大家能夠對C# 4.0中提出來的動態類型特性可以有進一步的了解,并且本專題也是這個系列中的最后一篇文章了,到這里C#基礎知識系列也就結束了,后面我會整理出這個系列文章的一個索引,從而方便大家收藏,然而C#4中對COM互操作性也有很大的改善,關于互操作的內容將會在后面一個系列文章中和大家分享下我的學習體會。眼看都2點20了,該睡覺去了。
- C# 基礎知識系列
- C# 基礎知識系列 專題一:深入解析委托——C#中為什么要引入委托
- C# 基礎知識系列 專題二:委托的本質論
- C# 基礎知識系列 專題三:如何用委托包裝多個方法——委托鏈
- C# 基礎知識系列 專題四:事件揭秘
- C# 基礎知識系列 專題五:當點擊按鈕時觸發Click事件背后發生的事情
- C# 基礎知識系列 專題六:泛型基礎篇——為什么引入泛型
- C# 基礎知識系列 專題七: 泛型深入理解(一)
- C# 基礎知識系列 專題八: 深入理解泛型(二)
- C# 基礎知識系列 專題九: 深入理解泛型可變性
- C#基礎知識系列 專題十:全面解析可空類型
- C# 基礎知識系列 專題十一:匿名方法解析
- C#基礎知識系列 專題十二:迭代器
- C#基礎知識 專題十三:全面解析對象集合初始化器、匿名類型和隱式類型
- C# 基礎知識系列 專題十四:深入理解Lambda表達式
- C# 基礎知識系列 專題十五:全面解析擴展方法
- C# 基礎知識系列 專題十六:Linq介紹
- C#基礎知識系列 專題十七:深入理解動態類型
- 你必須知道的異步編程 C# 5.0 新特性——Async和Await使異步編程更簡單
- 全面解析C#中參數傳遞
- C#基礎知識系列 全面解析C#中靜態與非靜態
- C# 基礎知識系列 C#中易混淆的知識點
- C#進階系列
- C#進階系列 專題一:深入解析深拷貝和淺拷貝
- C#進階系列 專題二:你知道Dictionary查找速度為什么快嗎?
- C# 開發技巧系列
- C# 開發技巧系列 使用C#操作Word和Excel程序
- C# 開發技巧系列 使用C#操作幻燈片
- C# 開發技巧系列 如何動態設置屏幕分辨率
- C# 開發技巧系列 C#如何實現圖片查看器
- C# 開發技巧 如何防止程序多次運行
- C# 開發技巧 實現屬于自己的截圖工具
- C# 開發技巧 如何使不符合要求的元素等于離它最近的一個元素
- C# 線程處理系列
- C# 線程處理系列 專題一:線程基礎
- C# 線程處理系列 專題二:線程池中的工作者線程
- C# 線程處理系列 專題三:線程池中的I/O線程
- C# 線程處理系列 專題四:線程同步
- C# 線程處理系列 專題五:線程同步——事件構造
- C# 線程處理系列 專題六:線程同步——信號量和互斥體
- C# 多線程處理系列專題七——對多線程的補充
- C#網絡編程系列
- C# 網絡編程系列 專題一:網絡協議簡介
- C# 網絡編程系列 專題二:HTTP協議詳解
- C# 網絡編程系列 專題三:自定義Web服務器
- C# 網絡編程系列 專題四:自定義Web瀏覽器
- C# 網絡編程系列 專題五:TCP編程
- C# 網絡編程系列 專題六:UDP編程
- C# 網絡編程系列 專題七:UDP編程補充——UDP廣播程序的實現
- C# 網絡編程系列 專題八:P2P編程
- C# 網絡編程系列 專題九:實現類似QQ的即時通信程序
- C# 網絡編程系列 專題十:實現簡單的郵件收發器
- C# 網絡編程系列 專題十一:實現一個基于FTP協議的程序——文件上傳下載器
- C# 網絡編程系列 專題十二:實現一個簡單的FTP服務器
- C# 互操作性入門系列
- C# 互操作性入門系列(一):C#中互操作性介紹
- C# 互操作性入門系列(二):使用平臺調用調用Win32 函數
- C# 互操作性入門系列(三):平臺調用中的數據封送處理
- C# 互操作性入門系列(四):在C# 中調用COM組件
- CLR
- 談談: String 和StringBuilder區別和選擇
- 談談:程序集加載和反射
- 利用反射獲得委托和事件以及創建委托實例和添加事件處理程序
- 談談:.Net中的序列化和反序列化
- C#設計模式
- UML類圖符號 各種關系說明以及舉例
- C#設計模式(1)——單例模式
- C#設計模式(2)——簡單工廠模式
- C#設計模式(3)——工廠方法模式
- C#設計模式(4)——抽象工廠模式
- C#設計模式(5)——建造者模式(Builder Pattern)
- C#設計模式(6)——原型模式(Prototype Pattern)
- C#設計模式(7)——適配器模式(Adapter Pattern)
- C#設計模式(8)——橋接模式(Bridge Pattern)
- C#設計模式(9)——裝飾者模式(Decorator Pattern)
- C#設計模式(10)——組合模式(Composite Pattern)
- C#設計模式(11)——外觀模式(Facade Pattern)
- C#設計模式(12)——享元模式(Flyweight Pattern)
- C#設計模式(13)——代理模式(Proxy Pattern)
- C#設計模式(14)——模板方法模式(Template Method)
- C#設計模式(15)——命令模式(Command Pattern)
- C#設計模式(16)——迭代器模式(Iterator Pattern)
- C#設計模式(17)——觀察者模式(Observer Pattern)
- C#設計模式(18)——中介者模式(Mediator Pattern)
- C#設計模式(19)——狀態者模式(State Pattern)
- C#設計模式(20)——策略者模式(Stragety Pattern)
- C#設計模式(21)——責任鏈模式
- C#設計模式(22)——訪問者模式(Vistor Pattern)
- C#設計模式(23)——備忘錄模式(Memento Pattern)
- C#設計模式總結
- WPF快速入門系列
- WPF快速入門系列(1)——WPF布局概覽
- WPF快速入門系列(2)——深入解析依賴屬性
- WPF快速入門系列(3)——深入解析WPF事件機制
- WPF快速入門系列(4)——深入解析WPF綁定
- WPF快速入門系列(5)——深入解析WPF命令
- WPF快速入門系列(6)——WPF資源和樣式
- WPF快速入門系列(7)——深入解析WPF模板
- WPF快速入門系列(8)——MVVM快速入門
- WPF快速入門系列(9)——WPF任務管理工具實現
- ASP.NET 開發
- ASP.NET 開發必備知識點(1):如何讓Asp.net網站運行在自定義的Web服務器上
- ASP.NET 開發必備知識點(2):那些年追過的ASP.NET權限管理
- ASP.NET中實現回調
- 跟我一起學WCF
- 跟我一起學WCF(1)——MSMQ消息隊列
- 跟我一起學WCF(2)——利用.NET Remoting技術開發分布式應用
- 跟我一起學WCF(3)——利用Web Services開發分布式應用
- 跟我一起學WCF(3)——利用Web Services開發分布式應用
- 跟我一起學WCF(4)——第一個WCF程序
- 跟我一起學WCF(5)——深入解析服務契約 上篇
- 跟我一起學WCF(6)——深入解析服務契約 下篇
- 跟我一起學WCF(7)——WCF數據契約與序列化詳解
- 跟我一起學WCF(8)——WCF中Session、實例管理詳解
- 跟我一起學WCF(9)——WCF回調操作的實現
- 跟我一起學WCF(10)——WCF中事務處理
- 跟我一起學WCF(11)——WCF中隊列服務詳解
- 跟我一起學WCF(12)——WCF中Rest服務入門
- 跟我一起學WCF(13)——WCF系列總結
- .NET領域驅動設計實戰系列
- .NET領域驅動設計實戰系列 專題一:前期準備之EF CodeFirst
- .NET領域驅動設計實戰系列 專題二:結合領域驅動設計的面向服務架構來搭建網上書店
- .NET領域驅動設計實戰系列 專題三:前期準備之規約模式(Specification Pattern)
- .NET領域驅動設計實戰系列 專題四:前期準備之工作單元模式(Unit Of Work)
- .NET領域驅動設計實戰系列 專題五:網上書店規約模式、工作單元模式的引入以及購物車的實現
- .NET領域驅動設計實戰系列 專題六:DDD實踐案例:網上書店訂單功能的實現
- .NET領域驅動設計實戰系列 專題七:DDD實踐案例:引入事件驅動與中間件機制來實現后臺管理功能
- .NET領域驅動設計實戰系列 專題八:DDD案例:網上書店分布式消息隊列和分布式緩存的實現
- .NET領域驅動設計實戰系列 專題九:DDD案例:網上書店AOP和站點地圖的實現
- .NET領域驅動設計實戰系列 專題十:DDD擴展內容:全面剖析CQRS模式實現
- .NET領域驅動設計實戰系列 專題十一:.NET 領域驅動設計實戰系列總結