探究 C# dynamic動態(tài)類型本質(zhì)
在做接口動態(tài)傳參的時候思考了個問題:如何把一個json字符串,轉(zhuǎn)成C#動態(tài)類?
比如由
{
'userId': 100,
'id': 1,
'title': 'hello world',
'completed': false
}
生成
dynamic obj = new
{
userId = 100,
id = 1,
title = "hello world",
completed = false,
};
解決這個問題前,我們先來了解一下dynamic動態(tài)類型。
動態(tài)類型是什么?
首先動態(tài)類型是靜態(tài)類,不是一種稱之為“動態(tài)”的類型,只不過這個類型的對象會跳過靜態(tài)類型檢查。
也就是在編譯過程中不報錯,但是運行程序?qū)ο蟪跏蓟螅撌鞘裁搭愋停敲催€是什么類型。
看個例子,有兩個動態(tài)類型obj1,obj2
dynamic obj1 = new
{
userId = 100,
id = 1,
title = "hello world",
completed = false,
};
dynamic obj2 = new System.Dynamic.ExpandoObject();
result.userId = 100;
result.id = 1;
result.title = "hello world";
result.completed = false;
Console.WriteLine("---obj1---");
Console.WriteLine(obj1.userId);
Console.WriteLine(obj1.id);
Console.WriteLine(obj1.title);
Console.WriteLine(obj1.completed);
Console.WriteLine("\n---obj2---");
Console.WriteLine(obj2.userId);
Console.WriteLine(obj2.id);
Console.WriteLine(obj2.title);
Console.WriteLine(obj2.completed);
運行結(jié)果如下
他們輸出的結(jié)果一樣,但你認為他們的返回結(jié)果是一樣的嗎?
obj1是一個類型為AnonymousType<int,int,string,bool>的匿名類,我們可以很輕松地通過反射的方式遍歷其成員變量:
Type t = obj1.GetType();
PropertyInfo[] pi = t.GetProperties();
foreach (PropertyInfo p in pi)
{
var key = p.Name;
var value = p.GetValue(obj1, null);
Console.WriteLine(key + ": " + value);
}
打印如下:
userId: 100
id: 1
title: hello world
completed: False
而obj2則是System.Dynamic.ExpandoObject類型的對象,而且從初始化到對象生命周期結(jié)束。始終是這個類型。
我們對obj2運行同樣的代碼,發(fā)現(xiàn)會報錯
Type t = obj2.GetType();
PropertyInfo[] pi = t.GetProperties();
foreach (PropertyInfo p in pi)
{
var key = p.Name;
var value = p.GetValue(obj1, null);
Console.WriteLine(key + ": " + value);
}
報錯的原因是obj2并不包含真正的userId成員變量,因為其本質(zhì)是個ExpandoObject對象,
可見dynamic關(guān)鍵字并不會改變C#變量在運行時的類型,它僅僅是在編譯階段跳過了靜態(tài)類型檢查。
動態(tài)類型的特點是什么?
然而你是可以通過重新賦值改變類型的,當然這是公共語言運行時 (CLR) 提供的動態(tài)技術(shù)。
dynamic number = 1;
Console.WriteLine(number.GetType()); //輸出System.Int
number = "text";
Console.WriteLine(number.GetType()); //輸出System.String
當我用ILspy反編譯工具查看IL源碼的時候,竟發(fā)現(xiàn)number變量的類型是object,也就是整個過程經(jīng)過了裝箱拆箱,經(jīng)過了從內(nèi)存棧創(chuàng)建地址引用到堆中區(qū)域的改變。dynamic幫我們完成了這些動作。
所以本質(zhì)上內(nèi)存中同一個對象不會平白無故從int類型轉(zhuǎn)換為string。畢竟C#不能像其他弱類型語言那樣使用。
obj1匿名類的成員變量是只讀的。給它賦一個其他類型的值,將會報錯;而給obj2的成員變量賦其他類型的值,則不會報錯。
obj1.userId = "100"; //運行時報錯
obj2.userId = "100";
在來看obj2,因為System.Dynamic.ExpandoObject 類型因?qū)崿F(xiàn)了 IDynamicMetaObjectProvider 因此它能通過.成員變量的方式訪問內(nèi)容。
又因為System.Dynamic.ExpandoObject實現(xiàn)了IDictionary<string, object?>因此可以通過向字典添加KeyValue對象的形式向ExpandoObject對象添加成員變量,用[key]方式訪問內(nèi)容。代碼如下
foreach (var entry in obj1)
{
(obj2 as IDictionary<string, object>).Add(entry.Key, entry.Value.ToString());
}
通過.成員變量的方式訪問內(nèi)容,可以說這是偽裝的成員變量。但稍微一測試,就露餡了。
動態(tài)類型如何用?
現(xiàn)在我們來回答“如何把一個json字符串,轉(zhuǎn)成C#動態(tài)類”這個問題,答案是做不到。
首先用Newtonsoft.Json庫轉(zhuǎn)換的結(jié)果,無論是用JObject.Parse(json)還是JsonConvert.DeserializeObject(json)最后返回的結(jié)果是JToken類型的對象, 通過反編譯Newtonsoft.Json.dll,查看JToken類型,可見它還是一個繼承了IDictionary<string, object?>和IDynamicMetaObjectProvider的類型,
string json = @"{
'userId': 100,
'id': 1,
'title': 'hello world',
'completed': false
}";
var obj1 = JObject.Parse(json);
dynamic obj2 = new System.Dynamic.ExpandoObject();
foreach (var entry in obj1)
{
(obj2 as IDictionary<string, object>).Add(entry.Key, entry.Value.ToString());
}
運行如上同樣的代碼檢查obj2
Type t = obj2.GetType();
PropertyInfo[] pi = t.GetProperties();
foreach (PropertyInfo p in pi)
{
var key = p.Name;
var value = p.GetValue(obj1, null);
Console.WriteLine(key + ": " + value);
}
可以通過這樣向obj2動態(tài)添加成員變量,但是始終是字典方式提供的偽對象。
轉(zhuǎn)自:林曉lx
鏈接:cnblogs.com/jevonsflash/p/17232450.html
