探究C# dynamic动态类型本质
本周在做接口动态传参的时候思考了个问题:如何把一个json字符串,转成C#动态类?
比如由
{
'userId': 100,
'id': 1,
'title': 'hello world',
'completed': false
}
生成
dynamic obj = new
{
userId = 100,
id = 1,
title = "hello world",
completed = false,
};
解决这个问题前,我们先来了解一下dynamic
动态类型。
动态类型是什么?
首先动态类型是静态类,不是一种称之为“动态”的类型,只不过这个类型的对象会跳过静态类型检查。也就是在编译过程中不报错,但是运行程序将对象初始化之后,它该是什么类型,那么还是什么类型。
看个例子,有两个动态类型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);
运行结果如下
他们输出的结果一样,但你认为他们的返回结果是一样的吗?
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
类型的对象,而且从初始化到对象生命周期结束。始终是这个类型。
我们对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
并不包含真正的userId
成员变量,因为其本质是个ExpandoObject
对象,
可见dynamic
关键字并不会改变C#变量在运行时的类型,它仅仅是在编译阶段跳过了静态类型检查。
动态类型的特点是什么?
然而你是可以通过重新赋值改变类型的,当然这是公共语言运行时 (CLR) 提供的动态技术。
dynamic number = 1;
Console.WriteLine(number.GetType()); //输出System.Int
number = "text";
Console.WriteLine(number.GetType()); //输出System.String
当我用ILspy反编译工具查看IL源码的时候,竟发现number
变量的类型是object
,也就是整个过程经过了装箱拆箱,经过了从内存栈创建地址引用到堆中区域的改变。dynamic
帮我们完成了这些动作。所以本质上内存中同一个对象不会平白无故从int
类型转换为string
。毕竟C#不能像其他弱类型语言那样使用。
obj1
匿名类的成员变量是只读的。给它赋一个其他类型的值,将会报错;
而给obj2
的成员变量赋其他类型的值,则不会报错。
obj1.userId = "100"; //运行时报错
obj2.userId = "100";
在来看obj2,因为System.Dynamic.ExpandoObject
类型因实现了 IDynamicMetaObjectProvider
因此它能通过.成员变量
的方式访问内容。
又因为System.Dynamic.ExpandoObject
实现了IDictionary<string, object?>
因此可以通过向字典添加KeyValue对象的形式向ExpandoObject
对象添加成员变量,用[key]
方式访问内容。代码如下
foreach (var entry in obj1)
{
(obj2 as IDictionary<string, object>).Add(entry.Key, entry.Value.ToString());
}
通过.成员变量
的方式访问内容,可以说这是伪装的成员变量。但稍微一测试,就露馅了。
动态类型如何用?
现在我们来回答“如何把一个json字符串,转成C#动态类”这个问题,答案是做不到。
首先用Newtonsoft.Json
库转换的结果,无论是用JObject.Parse(json)
还是JsonConvert.DeserializeObject(json)
最后返回的结果是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
动态添加成员变量,但是始终是字典方式提供的伪对象。
探究C# dynamic动态类型本质的更多相关文章
- c# 把一个匿名对象赋值给一个Object类型的变量后,怎么取这个变量? c# dynamic动态类型和匿名类 详解C# 匿名对象(匿名类型)、var、动态类型 dynamic 深入浅析C#中的var和dynamic
比如有一个匿名对象,var result =......Select( a=>new { id=a.id, name=a.name});然后Object obj = result ;我怎 ...
- Net 4.0 之 Dynamic 动态类型
Net 4.0 之 Dynamic 动态类型 本文主要旨在与网友分享.Net4.0的Dynamic 对Duck Type 的支持. 一..net4.0主要新特性 .Net4.0在.Net3.5 ...
- C#基本语法 - .Net 4.0 之 Dynamic 动态类型
一..net4.0主要新特性 .Net4.0在.Net3.5基础上新增的主要特性有:可选参数.命名参数和Dynamic.具体请阅生鱼片的这篇博文.这里我们着重讲解C#4.0的Dynamic特性,对 ...
- c# dynamic动态类型和匿名类
dynamic类型 简单示例 dynamic expando = new System.Dynamic.ExpandoObject(); //动态类型字段 可读可写 expando.Id = 1; e ...
- Json.net实现方便的Json转C#(dynamic动态类型)对象
以前需要将一段json字符串转换为C#对象时,一般都是定义一个与之对应的实体类来接收.这样做有一个很大的缺点,就是当字符串特别长,属性特别多,又有嵌套时,手敲这个实体类就非常痛苦. 比如之前做的一个接 ...
- dynamic动态类型的扩展方法
对于一个动态类型来说,你可以认为它包含任意成员,它们都能通过编译.但到了运行时,到底是否拥有这些成员,就真相大白了.如 dynamic test = ; Console.Write(test.Name ...
- C# 匿名对象(匿名类型)、var、动态类型 dynamic
本文是要写的下篇<C#反射及优化用法>的前奏,不能算是下一篇文章的基础的基础吧,有兴趣的朋友可以关注一下. 随着C#的发展,该语音内容不断丰富,开发变得更加方便快捷,C# 的锋利尽显无疑. ...
- C# 匿名对象(匿名类型)、var、动态类型 dynamic——实用之:过滤类属性、字段实用dynamic
例子 返回一个LIst<oject>类型 而oject含有 30个字段 而我只需要两个字段.这里实用dynamic 和 linq. 上代码: 注意select new {} 为匿名类型,这 ...
- dynamic:动态类型简单用法,写法
class 动态创建数据 { //动态类型:本质感觉跟object的用法差不多,只是在执行的时候才知道数据类型 public dynamic Dynamic() { //定义一个动态类型,作为返回值 ...
- var和dynamic的应用 var、动态类型 dynamic 深入浅析C#中的var和dynamic ----demo
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...
随机推荐
- TienChin 渠道管理-渠道页面完善
最后附上渠道管理的数据 install SQL 语句: INSERT INTO TienChin.tienchin_channel (channel_id, channel_name, status, ...
- 文档级关系抽取:基于结构先验产生注意力偏差SSAN模型
文档级关系抽取:基于结构先验产生注意力偏差SSAN模型 Entity Structure Within and Throughout: Modeling Mention Dependencies fo ...
- 多路转接高性能IO服务器|select|poll|epoll|模型详细实现
前言 那么这里博主先安利一下一些干货满满的专栏啦! Linux专栏https://blog.csdn.net/yu_cblog/category_11786077.html?spm=1001.2014 ...
- 京豆薅羊毛新姿势-docker方式
背景 上周看脉脉的时候看到下面这个帖子,领导让搞项目容器化,但是楼主没搞过,对新东西有畏惧感,怂了,然后把机会白白送给其他同事了. 想来我也是差不多这样的,刚到阿里工作的时候,有个好的项目机会来了,领 ...
- 小知识:RMAN备份当前控制文件报错ORA-245
在一个备份的case上遇到备份控制文件报错ORA-245,最终通过修改snapshot controlfile默认位置到ASM磁盘组后解决. 1.问题复现 回来后就想快速记录下这个小知识点,打开尘封的 ...
- list集合去重_谈谈Java中Set集合去重的原理
导读:Java中Set接口是Collectio的子接口,Set集合不允许包含相同的元素.如果添加相同的元素, add()会返回FALSE, 新元素不会加入.Set集合常用于元素为数字.字符串去重等,但 ...
- 大数据技术之DataX
一.DataX简介 DataX 是阿里巴巴开源的一个异构数据源离线同步工具,致力于实现包括关系型数据库(MySQL.Oracle等).HDFS.Hive.ODPS.HBase.FTP等各种异构数据源之 ...
- Linux通过fdisk或者parted进行磁盘分区,然后格式化和挂载磁盘
磁盘分区是Linux的常用命令,其中fdisk和parted命令最为常用.但是当磁盘大于2T时,fdisk只能分到2T. 比如4T的磁盘,fdisk只能分2T的主分区出来,parted可以直接分成4T ...
- NC50500 凸多边形的划分
题目链接 题目 题目描述 给定一个具有N个顶点的凸多边形,将顶点从1至N标号,每个顶点的权值都是一个正整数.将这个凸多边形划分成N-2个互不相交的三角形,试求这些三角形顶点的权值乘积和至少为多少. 输 ...
- Python Print 显示颜色
1.顺序:显示方式,前景颜色,背景颜色 2.顺序非固定,但尽量按默认书写方式 3.也可以在input中输出使用 4.格式: print('\033[显示方式:前景颜色:背景颜色m .......... ...