什麼是 DI Container?#
DI Container 是一種軟體函式庫,能夠自動化 DI 的三個核心維度:
- Object Composition——自動組裝物件圖
- Interception——攔截並擴充行為
- Lifetime Management——管理物件的生命週期
其核心 API 非常簡潔:透過 Resolve(Type) 或 Resolve<T>() 方法,容器即可回傳一個完整組裝好的物件圖。
Auto-Wiring:DI Container 的核心價值#
Auto-Wiring 是 DI Container 最重要的功能,也是它與 Pure DI 最大的區別:
- 容器會自動分析每個類別的建構函式參數
- 遞迴地解析每一個 Dependency,直到整個物件圖完成
- 開發者只需告訴容器「哪些類型映射到哪些介面」,組裝邏輯完全自動化

Figure 12.2: Auto-Wiring 的簡化工作流程
Auto-Wiring 的價值在於:當你新增一個 Dependency 時,不需要手動修改 Composition Root 中的組裝程式碼——容器會自動處理。

Figure 12.3: Composition Root 向容器請求 HomeController,容器遞迴呼叫自身以解析依賴
三種配置方式#
DI Container 提供三種不同的配置途徑,各有優缺點:
1. Configuration Files(XML / JSON)#
- 以外部設定檔定義型別映射
- 優點:支援 Late Binding,不需重新編譯即可更換實作
- 缺點:冗長、容易出錯、沒有編譯期檢查
2. Configuration as Code#
- 以程式碼明確註冊型別映射
- 優點:有編譯期檢查、IDE 支援重構
- 缺點:需要手動逐一註冊
- 這是最常見的配置方式
container.Register<IProductService, ProductService>();
container.Register<IProductRepository, SqlProductRepository>();3. Auto-Registration(Convention over Configuration)#
- 掃描 Assembly,依照慣例自動批次註冊
- 例如:「所有實作
ICommandService的類別,自動註冊為其介面」 - 這是 DI Container 相對於 Pure DI 的最大優勢
container.RegisterAssemblyTypes(assembly)
.Where(t => t.Name.EndsWith("Service"))
.AsImplementedInterfaces();Auto-Registration 是選擇 DI Container 而非 Pure DI 的主要理由。如果你的應用程式無法從 Auto-Registration 中獲益,使用 Pure DI 可能是更好的選擇。

Figure 12.5: DI Container 三種常見配置方式的明確性與綁定程度比較
DI Container vs Pure DI:如何選擇?#
DI Container 的成本#
- 學習曲線——需要學習特定容器的 API 和配置方式
- 第三方依賴——引入了額外的函式庫依賴
- 執行期錯誤——配置錯誤只能在執行時被發現
- 誤用風險——錯誤使用容器(如 Service Locator)可能比不用更糟
Pure DI 的優勢#
- 編譯期錯誤——組裝邏輯就是普通程式碼,錯誤在編譯時發現
- 零依賴——不需要任何第三方函式庫
- 更簡單——沒有「神奇」的行為,所有邏輯一目了然
作者的建議#
| 場景 | 建議 |
|---|---|
| 大型應用程式,大量相似的註冊邏輯 | 使用 DI Container + Auto-Registration |
| 中小型應用程式 | Pure DI 已足夠 |
| 團隊對 DI Container 不熟悉 | 從 Pure DI 開始 |

Figure 12.6: Pure DI 因其簡單性而有價值,DI Container 則取決於使用方式
書中的核心觀點:「DI Container 是一個有用但可選的工具。」 它不是 DI 的必要條件,Pure DI 本身已是完全正當的做法。
簡易 DI Container 實作#
書中提供了一個簡化版的 DI Container 實作,展示 Auto-Wiring 的運作原理:
public object Resolve(Type type)
{
// 查找是否有已註冊的映射
Type concreteType = registrations.ContainsKey(type)
? registrations[type]
: type;
// 取得建構函式
var ctor = concreteType.GetConstructors().Single();
// 遞迴解析每個參數
var parameters = ctor.GetParameters()
.Select(p => Resolve(p.ParameterType))
.ToArray();
return ctor.Invoke(parameters);
}這段程式碼清楚地展示了 Auto-Wiring 的核心邏輯:
- 使用 Reflection 檢查建構函式參數
- 遞迴呼叫
Resolve解析每一個依賴 - 最終組裝出完整的物件圖
關鍵結論
DI Container 的價值不在於取代 Pure DI,而在於當應用程式規模夠大時,Auto-Registration 能大幅減少重複的註冊程式碼。選擇 DI Container 與否,取決於 Auto-Registration 是否能為你的專案帶來實質幫助。