前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C#dynamic动态类型本质探究

C#dynamic动态类型本质探究

作者头像
郑子铭
发布2023-08-30 11:21:57
4520
发布2023-08-30 11:21:57
举报
文章被收录于专栏:DotNet NB && CloudNative

前言

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

比如由

代码语言:javascript
复制
{
    'userId': 100,
    'id': 1,
    'title': 'hello world',
    'completed': false
}

生成

代码语言:javascript
复制
dynamic obj = new
{
    userId = 100,
    id = 1,
    title = "hello world",
    completed = false,
};

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

动态类型是什么?

首先动态类型是静态类,不是一种称之为“动态”的类型,只不过这个类型的对象会跳过静态类型检查。

也就是在编译过程中不报错,但是运行程序将对象初始化之后,它该是什么类型,那么还是什么类型。

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

代码语言:javascript
复制
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>的匿名类,我们可以很轻松地通过反射的方式遍历其成员变量:

代码语言:javascript
复制
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);

}

打印如下:

代码语言:javascript
复制
userId: 100
id: 1
title: hello world
completed: False

obj2则是System.Dynamic.ExpandoObject类型的对象,而且从初始化到对象生命周期结束。始终是这个类型。

我们对obj2运行同样的代码,发现会报错

代码语言:javascript
复制
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) 提供的动态技术。

代码语言:javascript
复制
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的成员变量赋其他类型的值,则不会报错。

代码语言:javascript
复制
obj1.userId = "100"; //运行时报错
obj2.userId = "100";

在来看obj2,因为System.Dynamic.ExpandoObject 类型因实现了 IDynamicMetaObjectProvider 因此它能通过.成员变量的方式访问内容。

又因为System.Dynamic.ExpandoObject实现了IDictionary<string, object?>因此可以通过向字典添加KeyValue对象的形式向ExpandoObject对象添加成员变量,用[key]方式访问内容。代码如下

代码语言:javascript
复制
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的类型,

代码语言:javascript
复制
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

代码语言:javascript
复制
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动态添加成员变量,但是始终是字典方式提供的伪对象。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-08-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 DotNet NB 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
腾讯云代码分析
腾讯云代码分析(内部代号CodeDog)是集众多代码分析工具的云原生、分布式、高性能的代码综合分析跟踪管理平台,其主要功能是持续跟踪分析代码,观测项目代码质量,助力维护团队卓越代码文化。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档