由C# dynamic是否装箱引发的思考
前言
前几天在技术群里看到有同学在讨论关于dynamic是否会存在装箱拆箱的问题,我当时第一想法是"会"。至于为啥会有很多人有这种疑问,主要是因为觉得dynamic可能是因为有点特殊,因为它被称为动态类型,可能是因为这里的动态对大家造成的误解,认为这里的动态可以推断出具体的类型,所以可以避免装箱拆箱。但是事实并不是这样,今天就一起就这个问题虽然讨论一下。
装箱拆箱
首先咱们先来看下何为装箱拆箱,这个可以在微软官方文档中Boxing and Unboxing文档中看到答案,咱们就简单的摘要一下相关的描述
装箱是将值类型转换为类型对象或此值类型实现的任何接口类型的过程。当公共语言运行时 (CLR) 将值类型装箱时,它会将值包装在 System.Object 实例中并将其存储在托管堆上。拆箱从对象中提取值类型。拳击是隐含的;拆箱是明确的。装箱和拆箱的概念是 C# 类型系统统一视图的基础,其中任何类型的值都可以视为对象。
翻译起来会比较抽象,理解起来就是利用装箱和拆箱功能,可通过允许值类型的任何值与Object 类型的值相互转换,将值类型与引用类型链接起来。也就是值类型和引用类型相互转换的一做桥梁,但是问题也很明显那就是实例会存在在堆栈之前相互copy的问题,会存在一定的性能问题,所以这也一直是一个诟病。
虽然说是这样但是也没必要一直扣死角,毕竟很多时候程序还没有纠结到这种程度,因为任何语言存在的各种方法中或者操作中都会有一定这种问题,所以本质不是语言存在各种问题,而是在什么场景如何使用的问题。比如避免出现装箱和拆箱的办法也就是入概念所说的,那就是避免值类型和和引用类型之间相互转换,但是很多时候还是避免不了的,所以也不必纠结。
探究本质
上面讲解了关于装箱拆箱的概念,接下来咱们就来定义一段代码看看效果,为了方便对比咱们直接对比着看一下
dynamic num = 123;
dynamic str = "a string";
想要看清本质还是要反编译一下生成的结果看一下的,这里我们可以借助ILSpy或dnSpy来看下,首先看一下反编译回来的效果
private static void <Main>$(string[] args)
{
object num = 123;
object str = "a string";
Console.ReadKey();
}
因为我是使用的是.net6的顶级声明方式所以会生成<Main>$方法。不过从反编译的结果就可以看出来dynamic的本质是object,如果还有点怀疑的话可以直接查看生成的IL代码,还是使用ILSpy工具
.method private hidebysig static
void '<Main>$' (
string[] args
) cil managed
{
// Method begins at RVA 0x2094
// Header size: 12
// Code size: 30 (0x1e)
.maxstack 1
.entrypoint
.locals init (
// 这里可以看出声明的num和str变量都是object类型的
[0] object num,
[1] object str
)
// object obj = 123;
IL_0000: ldc.i4.s 123
// 这里的box说明存在装箱操作
IL_0002: box [System.Runtime]System.Int32
IL_0007: stloc.0
// object obj2 = "a string";
IL_0008: ldstr "a string"
IL_000d: stloc.1
// Console.ReadKey();
IL_000e: call valuetype [System.Console]System.ConsoleKeyInfo [System.Console]System.Console::ReadKey()
IL_0013: pop
// (no C# code)
IL_0014: nop
IL_0015: nop
IL_0016: nop
IL_0017: nop
IL_0018: nop
IL_0019: nop
IL_001a: nop
IL_001b: nop
// }
IL_001c: nop
IL_001d: ret
} // end of method Program::'<Main>$'
通过这里可以看出dynamic的本质确实是object,既然是object那就可以证实确实是存在装箱操作。这个其实在微软官方文档Using type dynamic上有说明,大致描述是这样的
dynamic类型是一种静态类型,但类型为dynamic的对象会跳过静态类型检查。大多数情况下,该对象就像具有类型object一样。 在编译时,将假定类型化为dynamic的元素支持任何操作。因此,不必考虑对象是从 COM API、从动态语言(例如 IronPython)、从 HTML 文档对象模型 (DOM)、从反射还是从程序中的其他位置获取自己的值。但是,如果代码无效,则在运行时会捕获到错误。
从这里可以看出dynamic表现出来的就是object,只是dynamic会跳过静态类型检查,所以编译的时候不会报错,有错误的话会在运行的时候报错,也就是我们说的是在运行时确定具体操作。这涉及到动态语言运行时,动态语言运行时(DLR)是一种运行时环境,可以将一组动态语言服务添加到公共语言运行时(CLR)。使用DLR可以轻松开发在.NET上运行的动态语言,并为静态类型语言添加动态特征。
匿名类型
总会有人拿dynamic和var进行比较,但是本质上来说,这两者描述的不是一个层面的东西。var叫隐式类型,本质是一种语法糖,也就是说在编译的时候就可以确定类型的具体类型,也就是说var本质是提供了一种更简单的编程体验,不会影响变量本身的行为。这也就解释了为啥同一个var变量多次赋值不能赋不同类型的值,比如以下操作编译器会直接报错
var num = 123;
num = "123"; //报错
如果你是用的集成开发环境的话其实很容易发现,把鼠标放到var类型上就会显示变量对应的真实类型。或者可以直接通过ILSpy看看反编译结果,比如声明了var num = 123编译完成之后就是
private static void <Main>$(string[] args)
{
int num = 123;
Console.ReadKey();
}
请注意这里并不是object而是转换成了具体的类型因为123就是int类型的,严谨一点看一下IL代码
.maxstack 1
.entrypoint
//声明的int32
.locals init (
[0] int32 num
)
// int num = 123;
IL_0000: ldc.i4.s 123
IL_0002: stloc.0
相信这里就可以看出来了dynamic和var确实也不是一个层面的东西。var是隐式类型是语法糖为了简化编程体验用的,dynamic则是动态语言运行时技术,编译时转换成object类型,因为在c#上一切都是object,然后再运行时进行具体的操作。
总结
本篇文章主要是在技术群里看到有同学在讨论关于dynamic是否会装箱引发的思考,相对来说讲解的比较基础也比较简单。想对一个东西理解的更透彻,就要一步一步的了解它到底是什么,这样的话就可以更好的理解和思考。也印证了那句话,你不会用或者用是因为你对它不够了解,当你对它有足够理解的时候,操作起来也就会游刃有余。
由C# dynamic是否装箱引发的思考的更多相关文章
- class_copyIvarList方法获取实例变量问题引发的思考
在runtime.h中,你可以通过其中的一个方法来获取实例变量,那就是class_copyIvarList方法,具体的实现如下: - (NSArray *)ivarArray:(Class)cls { ...
- Spring之LoadTimeWeaver——一个需求引发的思考---转
原文地址:http://www.myexception.cn/software-architecture-design/602651.html Spring之LoadTimeWeaver——一个需求引 ...
- 由SecureCRT引发的思考和学习
由SecureCRT引发的思考和学习 http://mp.weixin.qq.com/s?__biz=MzAxOTAzMDEwMA==&mid=2652500597&idx=1& ...
- 解决一道leetcode算法题的曲折过程及引发的思考
写在前面 本题实际解题过程是 从 40秒 --> 24秒 -->1.5秒 --> 715ms --> 320ms --> 48ms --> 36ms --> ...
- 【思考】由安装zabbix至排障php一系列引发的思考
[思考]由安装zabbix至排障php一系列引发的思考 linux的知识点林立众多,很有可能你在排查一个故障的时候就得用到另一门技术的知识: 由于linux本身的应用依赖的库和其它环境环环相扣,但又没 ...
- 由<a href = "#" > 引发的思考
原文:由<a href = "#" > 引发的思考 前阵子在一个移动项目中,通过 <a href = "#" > 的方式 绑定clic ...
- 曲演杂坛--一条DELETE引发的思考
原文:曲演杂坛--一条DELETE引发的思考 场景介绍: 我们有一张表,专门用来生成自增ID供业务使用,表结构如下: CREATE TABLE TB001 ( ID ,) PRIMARY KEY, D ...
- 由一个emoji引发的思考
由一个emoji引发的思考 从毕业以来,基本就一直在做移动端,但是一直就关于移动端的开发,各种适配问题的解决,在日常搬砖中处理了就过了,也没有把东西都沉淀下来,觉得甚是寒颜.现就一个小bug,让我们来 ...
- 一次composer错误使用引发的思考
一次composer错误使用引发的思考 这个思考源自于一个事故.让我对版本依赖重新思考了一下. 事故现象 一个线上的管理后台,一个使用laravel搭建的管理后台,之前在线上跑的好好的,今天comop ...
随机推荐
- 『忘了再学』Shell基础 — 6、Bash基本功能(输入输出重定向)
目录 1.Bash的标准输入输出 2.输出重定向 (1)标准输出重定向 (2)标准错误输出重定向 (3)正确输出和错误输出同时保存 3.输入重定向 1.Bash的标准输入输出 我们前边一直在说,在Li ...
- Python学习--21天Python基础学习之旅(Day08-Day21)
Day08: Chapter 11 异常 1.1如果try代码块中代码运行没有问题,则运行时跳过except代码块.(有异常时执行except中的代码,没有异常则跳过except中的代码) try-e ...
- word中怎么加入endnote的插件
首先,打开Microsoft Word 2010,然后点击文件菜单,在弹出的项目中点击选项. 2 弹出Word选项对话框,在左侧导航处点击"加载项"按钮,如图. 3 在右侧内容窗口 ...
- ionic的checkbox分析
之前分析了一个原生的checkbox,趁热打铁分析ionic的自带checkbox. html <label class="checkbox"> <input t ...
- 使用 jfinal + beetl + bootstrap 实现商城展示及管理系统
使用 jfinal+beetl 在线商品展示系统 使用 git clone https://github.com/zhouchong741/jfinal-demo.git 使用 IntelliJ ID ...
- python-使用函数求余弦函数的近似值
本题要求实现一个函数,用下列公式求cos(x)近似值,精确到最后一项的绝对值小于eps(绝对值小于eps的项不要加): cos(x)=0!x0−2!x2+4!x4−6!x6+... 函数接口定 ...
- leetcode921. 使括号有效的最少添加
题目描述: 给定一个由 '(' 和 ')' 括号组成的字符串 S,我们需要添加最少的括号( '(' 或是 ')',可以在任何位置),以使得到的括号字符串有效. 从形式上讲,只有满足下面几点之一,括号字 ...
- js修改html中class属性
document.getElementById("tr").setAttribute("class","styleclass"); 其中 ...
- uni-app 解析后台接口返回的HTML
正常使用rich-text是可以解决问题的,但是在支付宝小程序中不显示,在文档中看到" 支付宝小程序 nodes 属性只支持使用 Array 类型.如果需要支持 HTML String,则需 ...
- TINY语言采用递归下降分析法编写语法分析程序
目录 自顶向下分析方法 TINY文法 消左提左.构造first follow 基本思想 python构造源码 运行结果 参考来源:聊聊编译原理(二) - 语法分析 自顶向下分析方法 自顶向下分析方法: ...