C# dynamic類(lèi)型使用詳解
簡(jiǎn)介
C# 中的 dynamic 是一種特殊類(lèi)型,它允許在運(yùn)行時(shí)確定對(duì)象的類(lèi)型和成員,而不是在編譯時(shí)。
dynamic 的定義
dynamic是一種類(lèi)型,它告訴編譯器對(duì)其進(jìn)行“動(dòng)態(tài)類(lèi)型解析”。dynamic類(lèi)型的變量會(huì)跳過(guò)編譯時(shí)類(lèi)型檢查,所有的操作會(huì)推遲到運(yùn)行時(shí)進(jìn)行。- 適合處理未知類(lèi)型的對(duì)象,或需要與動(dòng)態(tài)語(yǔ)言(如
Python、JavaScript)互操作的場(chǎng)景。
dynamic 的使用
動(dòng)態(tài)類(lèi)型賦值
dynamic obj = 10; // 可以是整數(shù)
obj = "Hello"; // 可以變成字符串
obj = new { Name = "John", Age = 30 }; // 也可以是匿名類(lèi)型訪問(wèn)成員
動(dòng)態(tài)對(duì)象的成員在運(yùn)行時(shí)解析,因此可以訪問(wèn)任意成員:
如果訪問(wèn)了不存在的成員,運(yùn)行時(shí)會(huì)拋出 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)); // 輸出: 5dynamic 的核心特性
與編譯時(shí)類(lèi)型(靜態(tài)類(lèi)型)的區(qū)別:
- 編譯時(shí)檢查:
dynamic不會(huì)在編譯時(shí)檢查類(lèi)型或成員是否存在,所有操作推遲到運(yùn)行時(shí)。 - 靜態(tài)類(lèi)型:
object和其他類(lèi)型在編譯時(shí)進(jìn)行類(lèi)型檢查。
object obj1 = 10; // obj1.SomeMethod(); // 編譯錯(cuò)誤:object 沒(méi)有 SomeMethod 方法 dynamic obj2 = 10; // obj2.SomeMethod(); // 編譯通過(guò),但運(yùn)行時(shí)拋出異常
類(lèi)型推斷
動(dòng)態(tài)類(lèi)型在運(yùn)行時(shí)確定,而靜態(tài)類(lèi)型通過(guò)編譯器推斷:
dynamic dynamicVariable = 123; // 編譯器不檢查類(lèi)型 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í)解析,編譯通過(guò)
使用場(chǎng)景
- 與動(dòng)態(tài)語(yǔ)言交互:調(diào)用動(dòng)態(tài)語(yǔ)言的
API,如COM對(duì)象、IronPython等 JSON或XML數(shù)據(jù)處理:在處理結(jié)構(gòu)未知的數(shù)據(jù)時(shí)動(dòng)態(tài)解析。- 匿名類(lèi)型和動(dòng)態(tài)擴(kuò)展:快速訪問(wèn)動(dòng)態(tài)創(chuàng)建的對(duì)象。
注意事項(xiàng)
- 性能開(kāi)銷(xiāo):動(dòng)態(tài)綁定會(huì)引入性能開(kāi)銷(xiāo),因?yàn)榻馕鍪窃谶\(yùn)行時(shí)完成的。
- 類(lèi)型安全:缺乏編譯時(shí)類(lèi)型檢查,可能導(dǎo)致運(yùn)行時(shí)錯(cuò)誤。
- 調(diào)試?yán)щy:錯(cuò)誤可能難以發(fā)現(xiàn),尤其是在復(fù)雜場(chǎng)景中。
ExpandoObject 與 dynamic
ExpandoObject 是一個(gè)動(dòng)態(tài)對(duì)象,可在運(yùn)行時(shí)動(dòng)態(tài)添加或刪除成員:
常用于需要靈活擴(kuò)展的場(chǎng)景,如 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)用、成員訪問(wèn))的核心接口。IDictionary<string, object>:內(nèi)部使用一個(gè) Dictionary<string, object>存儲(chǔ)動(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)訪問(wèn)成員 // 等價(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)綁定和元對(duì)象來(lái)實(shí)現(xiàn)動(dòng)態(tài)行為:
- 動(dòng)態(tài)綁定:通過(guò)
IDynamicMetaObjectProvider,在運(yùn)行時(shí)解析屬性、方法等訪問(wèn)請(qǐng)求。 - 內(nèi)部字典:通過(guò)
Dictionary<string, object>存儲(chǔ)成員。 - 元對(duì)象:
ExpandoObject的動(dòng)態(tài)行為由一個(gè)元對(duì)象ExpandoMetaObject提供支持,它負(fù)責(zé)解釋動(dòng)態(tài)操作并將其映射到內(nèi)部字典。
ExpandoObject 線(xiàn)程安全性
ExpandoObject本質(zhì)上不是線(xiàn)程安全的,因?yàn)樗试S動(dòng)態(tài)修改成員。- 在多線(xiàn)程場(chǎng)景下,需要通過(guò)顯式鎖定來(lái)確保線(xiàn)程安全。
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查詢(xún)
因?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 的場(chǎng)景:
- 與外部動(dòng)態(tài)類(lèi)型(如
COM、動(dòng)態(tài)語(yǔ)言)交互。 - 動(dòng)態(tài)調(diào)用方法或訪問(wèn)屬性,無(wú)需構(gòu)建明確的對(duì)象。
- 臨時(shí)需要?jiǎng)討B(tài)行為,但不需要?jiǎng)討B(tài)修改成員。
2.使用 ExpandoObject 的場(chǎng)景:
- 需要構(gòu)建動(dòng)態(tài)擴(kuò)展的對(duì)象。
- 動(dòng)態(tài)添加和刪除屬性。
- 構(gòu)建輕量級(jí)、靈活的業(yè)務(wù)模型。
選擇建議
| 特性 | dynamic | ExpandoObject |
|---|---|---|
| 動(dòng)態(tài)成員解析 | 支持任意動(dòng)態(tài)成員,解析時(shí)運(yùn)行時(shí)檢查 | 只能操作明確添加到對(duì)象的動(dòng)態(tài)成員 |
| 動(dòng)態(tài)成員添加/刪除 | 不支持 | 支持(通過(guò)字典實(shí)現(xiàn)) |
| 類(lèi)型檢查 | 編譯時(shí)無(wú)類(lèi)型檢查,運(yùn)行時(shí)解析 | 同樣運(yùn)行時(shí)解析 |
| 適合的場(chǎng)景 | 動(dòng)態(tài)語(yǔ)言互操作、臨時(shí)操作、反射 | 動(dòng)態(tài)構(gòu)建對(duì)象、領(lǐng)域模型擴(kuò)展 |
| 性能 | 比靜態(tài)類(lè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)行為 |
| 對(duì)字典的支持 | 不支持 | 內(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#創(chuàng)建簡(jiǎn)單windows窗體應(yīng)用(加法器)
這篇文章主要為大家詳細(xì)介紹了C#創(chuàng)建一個(gè)簡(jiǎn)單windows窗體應(yīng)用的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-03-03
C#(asp.net)多線(xiàn)程用法示例(可用于同時(shí)處理多個(gè)任務(wù))
這篇文章主要介紹了C#(asp.net)多線(xiàn)程Thread用法,可用于同時(shí)處理多個(gè)任務(wù),以簡(jiǎn)單數(shù)學(xué)運(yùn)算為例講述了Thread類(lèi)實(shí)現(xiàn)多線(xiàn)程的相關(guān)技巧,需要的朋友可以參考下2016-06-06
C#數(shù)值轉(zhuǎn)換-隱式數(shù)值轉(zhuǎn)換表參考
隱式轉(zhuǎn)換就是直接使用,比如可以把一個(gè) byte 類(lèi)型直接用在 int 上2013-04-04
Unity游戲開(kāi)發(fā)中的中介者模式的應(yīng)用與實(shí)現(xiàn)
中介者模式是Unity游戲開(kāi)發(fā)中常用的設(shè)計(jì)模式之一,用于減少對(duì)象之間的直接耦合,通過(guò)引入中介者來(lái)協(xié)調(diào)對(duì)象之間的交互。通過(guò)中介者模式,游戲?qū)ο罂梢愿屿`活地進(jìn)行交互和協(xié)同工作,降低系統(tǒng)復(fù)雜度,使代碼更加易于維護(hù)和擴(kuò)展2023-05-05

