电厂建设审批进度网站,网站结构优化建议,有没有做兼职的网站吗,wordpress后台不能登陆前言前几天在技术群里看到有同学在讨论关于dynamic是否会存在装箱拆箱的问题,我当时第一想法是会。至于为啥会有很多人有这种疑问#xff0c;主要是因为觉得dynamic可能是因为有点特殊#xff0c;因为它被称为动态类型,可能是因为这里的动态对大家造成的误解,认为… 前言 前几天在技术群里看到有同学在讨论关于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.Int32IL_0007: stloc.0// object obj2 a string;IL_0008: ldstr a stringIL_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: nopIL_0015: nopIL_0016: nopIL_0017: nopIL_0018: nopIL_0019: nopIL_001a: nopIL_001b: nop// }IL_001c: nopIL_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是否会装箱引发的思考相对来说讲解的比较基础也比较简单。想对一个东西理解的更透彻就要一步一步的了解它到底是什么这样的话就可以更好的理解和思考。也印证了那句话你不会用或者用是因为你对它不够了解当你对它有足够理解的时候操作起来也就会游刃有余。引用链接[1] Boxing and Unboxing: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/types/boxing-and-unboxing[2] Using type dynamic: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/types/using-type-dynamic