C# dynamic類型使用詳解
簡介
C#
中的 dynamic
是一種特殊類型,它允許在運(yùn)行時(shí)確定對象的類型和成員,而不是在編譯時(shí)。
dynamic 的定義
dynamic
是一種類型,它告訴編譯器對其進(jìn)行“動(dòng)態(tài)類型解析”。dynamic
類型的變量會跳過編譯時(shí)類型檢查,所有的操作會推遲到運(yùn)行時(shí)進(jìn)行。- 適合處理未知類型的對象,或需要與動(dòng)態(tài)語言(如
Python
、JavaScript
)互操作的場景。
dynamic 的使用
動(dòng)態(tài)類型賦值
dynamic obj = 10; // 可以是整數(shù) obj = "Hello"; // 可以變成字符串 obj = new { Name = "John", Age = 30 }; // 也可以是匿名類型
訪問成員
動(dòng)態(tài)對象的成員在運(yùn)行時(shí)解析,因此可以訪問任意成員:
如果訪問了不存在的成員,運(yùn)行時(shí)會拋出 RuntimeBinderException
。
dynamic obj = new { Name = "John", Age = 30 }; Console.WriteLine(obj.Name); // 輸出: John
動(dòng)態(tài)方法調(diào)用
dynamic math = new { Add = (Func<int, int, int>)((x, y) => x + y) }; Console.WriteLine(math.Add(2, 3)); // 輸出: 5
dynamic 的核心特性
與編譯時(shí)類型(靜態(tài)類型)的區(qū)別:
- 編譯時(shí)檢查:
dynamic
不會在編譯時(shí)檢查類型或成員是否存在,所有操作推遲到運(yùn)行時(shí)。 - 靜態(tài)類型:
object
和其他類型在編譯時(shí)進(jìn)行類型檢查。
object obj1 = 10; // obj1.SomeMethod(); // 編譯錯(cuò)誤:object 沒有 SomeMethod 方法 dynamic obj2 = 10; // obj2.SomeMethod(); // 編譯通過,但運(yùn)行時(shí)拋出異常
類型推斷
動(dòng)態(tài)類型在運(yùn)行時(shí)確定,而靜態(tài)類型通過編譯器推斷:
dynamic dynamicVariable = 123; // 編譯器不檢查類型 int staticVariable = 123; // 編譯器推斷為 int object obj = "Hello"; // Console.WriteLine(obj.Length); // 編譯錯(cuò)誤 Console.WriteLine(((string)obj).Length); // 強(qiáng)制轉(zhuǎn)換 dynamic dyn = "Hello"; Console.WriteLine(dyn.Length); // 運(yùn)行時(shí)解析,編譯通過
使用場景
- 與動(dòng)態(tài)語言交互:調(diào)用動(dòng)態(tài)語言的
API
,如COM
對象、IronPython
等 JSON
或XML
數(shù)據(jù)處理:在處理結(jié)構(gòu)未知的數(shù)據(jù)時(shí)動(dòng)態(tài)解析。- 匿名類型和動(dòng)態(tài)擴(kuò)展:快速訪問動(dòng)態(tài)創(chuàng)建的對象。
注意事項(xiàng)
- 性能開銷:動(dòng)態(tài)綁定會引入性能開銷,因?yàn)榻馕鍪窃谶\(yùn)行時(shí)完成的。
- 類型安全:缺乏編譯時(shí)類型檢查,可能導(dǎo)致運(yùn)行時(shí)錯(cuò)誤。
- 調(diào)試?yán)щy:錯(cuò)誤可能難以發(fā)現(xiàn),尤其是在復(fù)雜場景中。
ExpandoObject 與 dynamic
ExpandoObject
是一個(gè)動(dòng)態(tài)對象,可在運(yùn)行時(shí)動(dòng)態(tài)添加或刪除成員:
常用于需要靈活擴(kuò)展的場景,如 JSON
數(shù)據(jù)的解析
using System.Dynamic; dynamic expando = new ExpandoObject(); expando.Name = "John"; expando.Age = 30; Console.WriteLine($"{expando.Name}, {expando.Age}");
ExpandoObject 內(nèi)部實(shí)現(xiàn)機(jī)制
ExpandoObject
實(shí)現(xiàn)了以下關(guān)鍵接口:
IDynamicMetaObjectProvider
:提供動(dòng)態(tài)行為(如動(dòng)態(tài)調(diào)用、成員訪問)的核心接口。IDictionary<string, object>
:內(nèi)部使用一個(gè) Dictionary<string, object>
存儲動(dòng)態(tài)添加的成員。
using System.Dynamic; dynamic expando = new ExpandoObject(); expando.Name = "John"; // 動(dòng)態(tài)添加成員 Console.WriteLine(expando.Name); // 動(dòng)態(tài)訪問成員 // 等價(jià)于: var expando = new ExpandoObject() as IDictionary<string, object>; expando["Name"] = "John"; Console.WriteLine(expando["Name"]);
ExpandoObject 如何實(shí)現(xiàn)動(dòng)態(tài)性?
ExpandoObject
使用動(dòng)態(tài)綁定和元對象來實(shí)現(xiàn)動(dòng)態(tài)行為:
- 動(dòng)態(tài)綁定:通過
IDynamicMetaObjectProvider
,在運(yùn)行時(shí)解析屬性、方法等訪問請求。 - 內(nèi)部字典:通過
Dictionary<string, object>
存儲成員。 - 元對象:
ExpandoObject
的動(dòng)態(tài)行為由一個(gè)元對象ExpandoMetaObject
提供支持,它負(fù)責(zé)解釋動(dòng)態(tài)操作并將其映射到內(nèi)部字典。
ExpandoObject 線程安全性
ExpandoObject
本質(zhì)上不是線程安全的,因?yàn)樗试S動(dòng)態(tài)修改成員。- 在多線程場景下,需要通過顯式鎖定來確保線程安全。
ExpandoObject 使用示例
- 動(dòng)態(tài)添加/刪除成員
dynamic expando = new ExpandoObject(); expando.Name = "John"; expando.Age = 30; // 刪除成員 var dict = (IDictionary<string, object>)expando; dict.Remove("Age"); // 檢查成員 Console.WriteLine(dict.ContainsKey("Name")); // True Console.WriteLine(dict.ContainsKey("Age")); // False
- 結(jié)合
LINQ
查詢
因?yàn)?ExpandoObject
實(shí)現(xiàn)了 IDictionary<string, object>
,可以結(jié)合 LINQ
操作:
dynamic expando = new ExpandoObject(); expando.Name = "John"; expando.Age = 30; var dict = (IDictionary<string, object>)expando; var filtered = dict.Where(kv => kv.Key.StartsWith("N")); foreach (var kv in filtered) { Console.WriteLine($"{kv.Key}: {kv.Value}"); }
綜合考慮
1.使用 dynamic
的場景:
- 與外部動(dòng)態(tài)類型(如
COM
、動(dòng)態(tài)語言)交互。 - 動(dòng)態(tài)調(diào)用方法或訪問屬性,無需構(gòu)建明確的對象。
- 臨時(shí)需要?jiǎng)討B(tài)行為,但不需要?jiǎng)討B(tài)修改成員。
2.使用 ExpandoObject
的場景:
- 需要構(gòu)建動(dòng)態(tài)擴(kuò)展的對象。
- 動(dòng)態(tài)添加和刪除屬性。
- 構(gòu)建輕量級、靈活的業(yè)務(wù)模型。
選擇建議
特性 | dynamic | ExpandoObject |
---|---|---|
動(dòng)態(tài)成員解析 | 支持任意動(dòng)態(tài)成員,解析時(shí)運(yùn)行時(shí)檢查 | 只能操作明確添加到對象的動(dòng)態(tài)成員 |
動(dòng)態(tài)成員添加/刪除 | 不支持 | 支持(通過字典實(shí)現(xiàn)) |
類型檢查 | 編譯時(shí)無類型檢查,運(yùn)行時(shí)解析 | 同樣運(yùn)行時(shí)解析 |
適合的場景 | 動(dòng)態(tài)語言互操作、臨時(shí)操作、反射 | 動(dòng)態(tài)構(gòu)建對象、領(lǐng)域模型擴(kuò)展 |
性能 | 比靜態(tài)類型慢,因?yàn)檫\(yùn)行時(shí)動(dòng)態(tài)綁定 | 更高效,基于字典實(shí)現(xiàn) |
實(shí)現(xiàn)復(fù)雜度 | 動(dòng)態(tài)行為由 CLR 處理 | 由 ExpandoObject 自行實(shí)現(xiàn)動(dòng)態(tài)行為 |
對字典的支持 | 不支持 | 內(nèi)部就是 IDictionary<string, object> |
安全性 | 運(yùn)行時(shí)錯(cuò)誤較多,調(diào)試復(fù)雜 | 動(dòng)態(tài)但有一定約束 |
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
C#/VB.NET實(shí)現(xiàn)從PPT中提取圖片的示例代碼
PPT是用于制作幻燈片(演示文稿)的應(yīng)用軟件,每張幻燈片中都可以包含文字、圖形、圖形、表格、聲音和影像等多種信息。本文主要介紹了如何實(shí)現(xiàn)從PPT中提取圖片的功能,需要的可以參考一下2023-03-03C#實(shí)現(xiàn)簡單的JSON序列化功能代碼實(shí)例
這篇文章主要介紹了C#實(shí)現(xiàn)簡單的JSON序列化功能,大家可以參考使用2013-11-11C# 數(shù)據(jù)驗(yàn)證Regex示例詳解
文章介紹了C#中使用Regex進(jìn)行數(shù)據(jù)驗(yàn)證的方法,包括整數(shù)和小數(shù)的正負(fù)驗(yàn)證,以及郵箱和身份證號的格式驗(yàn)證,感興趣的朋友一起看看吧2025-02-02Unity?UGUI的StandaloneInputModule標(biāo)準(zhǔn)輸入模塊組件使用示例
這篇文章主要為大家介紹了Unity?UGUI的StandaloneInputModule標(biāo)準(zhǔn)輸入模塊組件使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08C#與PHP的md5計(jì)算結(jié)果不同的解決方法
今天在用C#接入我的登錄api發(fā)現(xiàn)了一個(gè)問題,登陸的時(shí)候無論如何都會出現(xiàn)用戶名和密碼錯(cuò)誤的問題,后來通過查找排除找的了問題的原因是因?yàn)镃#與PHP的md5計(jì)算結(jié)果不同導(dǎo)致的,下面就來看看如何解決這個(gè)問題吧。2016-12-12C#?線程切換后上下文都去了哪里(.NET高級調(diào)試分析)
總會有一些朋友問一個(gè)問題,在 Windows 中線程做了上下文切換,請問被切的線程他的寄存器上下文都去了哪里?這個(gè)問題其實(shí)比較底層,如果對操作系統(tǒng)沒有個(gè)體系層面的理解以及做過源碼分析,其實(shí)很難說明白,這篇我們就從.NET高級調(diào)試的角度分析,需要的朋友可以參考下2023-12-12Visual C#類的定義及實(shí)現(xiàn)方法實(shí)例解析
這篇文章主要介紹了Visual C#類的定義及實(shí)現(xiàn)方法實(shí)例解析,對于新手來說有不錯(cuò)的借鑒學(xué)習(xí)價(jià)值,需要的朋友可以參考下2014-07-07