本周在做接口动态传参的时候思考了个问题:如何把一个json字符串,转成C#动态类?

比如由

{
'userId': 100,
'id': 1,
'title': 'hello world',
'completed': false
}

生成

dynamic obj = new
{
userId = 100,
id = 1,
title = "hello world",
completed = false,
};

解决这个问题前,我们先来了解一下dynamic动态类型。

动态类型是什么?

首先动态类型是静态类,不是一种称之为“动态”的类型,只不过这个类型的对象会跳过静态类型检查。也就是在编译过程中不报错,但是运行程序将对象初始化之后,它该是什么类型,那么还是什么类型。

看个例子,有两个动态类型obj1obj2

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动态类型本质的更多相关文章

  1. c# 把一个匿名对象赋值给一个Object类型的变量后,怎么取这个变量? c# dynamic动态类型和匿名类 详解C# 匿名对象(匿名类型)、var、动态类型 dynamic 深入浅析C#中的var和dynamic

    比如有一个匿名对象,var  result =......Select( a=>new {  id=a.id, name=a.name});然后Object  obj =  result ;我怎 ...

  2. Net 4.0 之 Dynamic 动态类型

    Net 4.0 之 Dynamic 动态类型 本文主要旨在与网友分享.Net4.0的Dynamic 对Duck Type 的支持.     一..net4.0主要新特性 .Net4.0在.Net3.5 ...

  3. C#基本语法 - .Net 4.0 之 Dynamic 动态类型

      一..net4.0主要新特性 .Net4.0在.Net3.5基础上新增的主要特性有:可选参数.命名参数和Dynamic.具体请阅生鱼片的这篇博文.这里我们着重讲解C#4.0的Dynamic特性,对 ...

  4. c# dynamic动态类型和匿名类

    dynamic类型 简单示例 dynamic expando = new System.Dynamic.ExpandoObject(); //动态类型字段 可读可写 expando.Id = 1; e ...

  5. Json.net实现方便的Json转C#(dynamic动态类型)对象

    以前需要将一段json字符串转换为C#对象时,一般都是定义一个与之对应的实体类来接收.这样做有一个很大的缺点,就是当字符串特别长,属性特别多,又有嵌套时,手敲这个实体类就非常痛苦. 比如之前做的一个接 ...

  6. dynamic动态类型的扩展方法

    对于一个动态类型来说,你可以认为它包含任意成员,它们都能通过编译.但到了运行时,到底是否拥有这些成员,就真相大白了.如 dynamic test = ; Console.Write(test.Name ...

  7. C# 匿名对象(匿名类型)、var、动态类型 dynamic

    本文是要写的下篇<C#反射及优化用法>的前奏,不能算是下一篇文章的基础的基础吧,有兴趣的朋友可以关注一下. 随着C#的发展,该语音内容不断丰富,开发变得更加方便快捷,C# 的锋利尽显无疑. ...

  8. C# 匿名对象(匿名类型)、var、动态类型 dynamic——实用之:过滤类属性、字段实用dynamic

    例子 返回一个LIst<oject>类型 而oject含有 30个字段 而我只需要两个字段.这里实用dynamic 和 linq. 上代码: 注意select new {} 为匿名类型,这 ...

  9. dynamic:动态类型简单用法,写法

    class 动态创建数据 { //动态类型:本质感觉跟object的用法差不多,只是在执行的时候才知道数据类型 public dynamic Dynamic() { //定义一个动态类型,作为返回值 ...

  10. var和dynamic的应用 var、动态类型 dynamic 深入浅析C#中的var和dynamic ----demo

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

随机推荐

  1. C++ Qt开发:运用QJSON模块解析数据

    Qt 是一个跨平台C++图形界面开发库,利用Qt可以快速开发跨平台窗体应用程序,在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置,实现图形化开发极大的方便了开发效率,本章将重点介绍如何运用QJs ...

  2. 5.3 Windows驱动开发:内核取应用层模块基址

    在上一篇文章<内核取ntoskrnl模块基地址>中我们通过调用内核API函数获取到了内核进程ntoskrnl.exe的基址,当在某些场景中,我们不仅需要得到内核的基地址,也需要得到特定进程 ...

  3. Linux 文件目录操作命令

    Linux 基础的文件目录操作命令,融合多部Linux经典著作,去除多余部分,保留实用部分. 显示目录或文件: 显示目标列表,在Linux系统中是使用率较高的命令.ls命令的输出信息可以进行彩色加亮显 ...

  4. Centos7 本地与网络Yum源配置

    虽然Yum工具,只需要你的主机连接互联网,就可以直接使用,但是有些时候我们需要自定义Yum源实现加速,下面我们将配置一个本地Yum源,来使用光盘中的软件,其他网络配置操作步骤相同,只是会在baseur ...

  5. 从嘉手札<2023-11-18>

    随便补一个~ 1.我也不是不快乐,我其实挺快乐的,和朋友出去玩,看电影,刷搞笑视频,我都能表现的很好,但这些都不是真正让我感受到快乐的东西,它就像膝跳反应一样,碰我一下我就会笑,但笑完就结束了.甚至在 ...

  6. Redis中的key的生存时间和过期时间

    目录 1.说明 2.指令 2.1.删除和更新 3.过期时间的保存 4.计算剩余生存时间 5.过期键的删除策略 5.1.定时删除 5.2.惰性删除 5.3.定期删除 1.说明 生存时间: (Time T ...

  7. java将集合里面的元素拼接为一条String字符串

    java将集合里面的元素拼接为一条String字符串 1️⃣ 随便创建一个list集合,往里面塞入元素 2️⃣ 第一种方式:通过foreach循环实现 但是通过这种方式只能将list集合里面的元素取出 ...

  8. (C语言)课后题之计算器

    #include <stdio.h> void main() { //定义两个算术变量,四个运算结果变量 int a,b,sum,sub,mul,mod; double div; prin ...

  9. 如何基于 spdlog 在编译期提供类 logrus 的日志接口

    如何基于 spdlog 在编译期提供类 logrus 的日志接口 实现见 Github,代码简单,只有一个头文件. 前提 几年前看到戈君在知乎上的一篇文章,关于打印日志的一些经验总结: 实践下来很受用 ...

  10. VMware 虚拟机一键去虚拟化工具

    前言: 如果你想在 VMware 虚拟机里面多开玩游戏的话,但是现在大多数网游都会检测是否虚拟机,进入游戏被检测到在虚拟机中运行,游戏可能直接闪退.所以就得对 VMware 虚拟机进行去除虚拟化. 原 ...