【C#学习笔记】装箱和拆箱

在这里插入图片描述

文章目录

  • 装箱和拆箱
    • 性能消耗
    • 装箱
    • 拆箱
  • 比较var,object,dynamic,\<T\>
    • var
    • object
    • \<T\> 泛型
    • dynamic


装箱和拆箱

在讲引用类型object的时候,我们说它是万能的,却没说它万能在哪里。

除了object为每一种变量类型提供了ToString,GetHashCode,Equals,GetType方法之外,object作为所有类型的父类,它可以实现任意变量类型到object的转换

一方面,使用object类型可以显式转换到任意类型,(其中没有发生装箱拆箱):

object arr=new int[10];
int[] ar=(int[])arr;object arr=new int[10];
int[] ar=arr as int[]; // as强转object类型为int数组

另一方面,我们将一个值类型转化为object(引用类型)的过程称为装箱,而将装箱的object转化回值类型的过程称为拆箱

下例将整型变量 i 进行了装箱并分配给对象 o。

int i = 123;
// The following line boxes i.
object o = i; //隐式装箱

然后,可以将对象 o 取消装箱并分配给整型变量 i:

o = 123;
i = (int)o;  // unboxing

下面是官方示例,展示了如何使用一个object的list来存储不同值类型的数据并对其分别进行操作:

Console.WriteLine(String.Concat("Answer", 42, true));
List<object> mixedList = new List<object>();
mixedList.Add("First Group:");for (int j = 1; j < 5; j++)
{mixedList.Add(j);
}mixedList.Add("Second Group:");
for (int j = 5; j < 10; j++)
{mixedList.Add(j);
}foreach (var item in mixedList)
{Console.WriteLine(item);
}var sum = 0;
for (var j = 1; j < 5; j++)
{sum += (int)mixedList[j] * (int)mixedList[j];// 注意在进行数值运算的时候不能用object直接运算,应当拆箱为原类型再计算
}
Console.WriteLine("Sum: " + sum);// Output:
// Answer42True
// First Group:
// 1
// 2
// 3
// 4
// Second Group:
// 5
// 6
// 7
// 8
// 9
// Sum: 30

性能消耗

相对于简单的赋值而言,装箱和取消装箱过程需要进行大量的计算。 对值类型进行装箱时,必须分配并构造一个新对象。 取消装箱所需的强制转换也需要进行大量的计算,只是程度较轻。

实际上,在编程中应当尽量避免装箱和拆箱,除非真的必须要使用。

如果一个变量被装箱引用后还需要被拆箱引用,倒不如直接赋值一个新的变量,因为对值类型进行装箱时,必须创建一个全新的对象, 这可能比简单的引用赋值用时最多长 20 倍。 取消装箱的过程所需时间可达赋值操作的四倍。

如果一个变量,一个方法需要频繁装箱拆箱,说明本身程序设计就存在问题。例如定义了一个接受任意输入类型的函数,但是所有的输入值类型在方法内都会被强制转化为object类型。就比如上面例子中定义的List<object>,我们每存入一个值就要被装箱一次。实际上定义一个泛型List<T>是更好的选择,当我们需要接收任意值类型变量时,应当使用泛型来代替object对其他类型的装箱。例如下面的例子,显然前者更好:

    public class Stack<T>{List<T> a = new List<T>();}public class Stack{List<object> b = new List<object>();}

装箱

装箱用于在垃圾回收堆中存储值类型。 装箱是值类型到 object 类型或到此值类型所实现的任何接口类型的隐式转换。 对值类型装箱会在堆中分配一个对象实例,并将该值复制到新的对象中。

此语句的结果是在堆栈上创建对象引用 o,而在堆上则引用 int 类型的值。 该值是赋给变量 i 的值类型值的一个副本。 以下装箱转换图说明了 i 和 o 这两个变量之间的差异:

在这里插入图片描述
(从上图中可以看到,原本i作为值类型存储在栈上,而object作为引用类型存储在堆上。当我们执行装箱操作的时候,一方面将值类型的装箱类型记录在了堆上,同时将堆上的object的值赋值为了123,并且在栈上同时创建了一个引用o用于引用堆上的object。)

这意味着在装箱的时候,object只是复制了i的值,而非它的地址本身,这也容易理解,因为值类型赋值的时候会重新创建一个地址,引用值类型是不可靠的:

class TestBoxing
{static void Main(){int i = 123;// Boxing copies the value of i into object o.object o = i;// Change the value of i.i = 456;// The change in i doesn't affect the value stored in o.System.Console.WriteLine("The value-type value = {0}", i);System.Console.WriteLine("The object-type value = {0}", o);}
}
/* Output:The value-type value = 456The object-type value = 123
*/

拆箱

取消装箱是从 object 类型到值类型或从接口类型到实现该接口的值类型的显式转换。 取消装箱操作包括:

  • 检查对象实例,以确保它是给定值类型的装箱值。

  • 将该值从实例复制到值类型变量中。
    在这里插入图片描述

从拆箱过程可以看出,实际上我们拆箱的时候,首先判断拆箱项o是否是对object的引用。然后判断拆箱类型是否是object的装箱类型,最后拆箱的值将会在栈中重新创建一个。

此外,拆箱也存在着隐式转换,但拆箱项类型必须相同,例如下面的例子:

float t =(int) o; //拆箱项正确,拆出的int可直接隐式转换为folat
int t =(float) o; //拆箱项错误

根据上述原理,如果想要改变一个已经装箱的object的值,唯一的方法只有先拆箱,再重新装箱。


比较var,object,dynamic,<T>

这四种方法看起来比较类似,都可以接收任意类型,但实际区别很大。

var

var的原理是基于编译器,当我们用var来定义变量类型时,只能用于局部变量,并且是让编译器从初始化表达式推断出变量的类型。

var a = 12;
a.IndexOf("1", 0, 2); //报错,编译器已经推测出a是int类型了,不能使用string的方法

var 的常见用途是用于构造函数调用表达式。当函数中的一些局部变量的赋值类型不明的时候,使用var来接收是安全的。使用var类型应当是最方便最随意的。

object

object虽然可以转化为任意类型,也可以通过装箱接收值类型的转换。优点是我们可以将object类型作为函数的返回类型,也可以在函数的入参中定义object类。但是它的优点也是它的缺点,假设函数里定义了object入参,鬼知道当别人使用函数的时候会传入什么东西进去,情况会变得越来越复杂。要么使用泛型,要么指定参数类型,在函数内部对其装箱。

因此,我们最好只在类型转换的时候使用object。如果想在函数定义一个可变类型,并且也不希望出现装箱拆箱,请使用<T>

<T> 泛型

泛型除了不能定义变量,其他都能定义。可以创建自己的泛型接口、泛型类、泛型方法、泛型事件和泛型委托。还可以对泛型类进行约束以访问特定数据类型的方法。

一个简单的泛型例子就是集合类提供的List<T>。这个东西有多好用就不用讲了。

使用泛型在各种类或方法中获取任意类型。泛型不用装箱拆箱。你可以将泛型理解成替换,在使用的时候将泛型参数替换成具体的类型,这个过程是在编译的时候进行的,使用泛型,编译器依然能够检测出类型错误。

dynamic

dynamic本身也是一个Object。在大多数情况下,dynamic 类型与 object 类型的行为类似。 具体而言,任何非 Null 表达式都可以转换为 dynamic 类型。 dynamic 类型与 object 的不同之处在于,编译器不会对包含类型 dynamic 的表达式的操作进行解析或类型检查。 编译器将有关该操作信息打包在一起,之后这些信息会用于在运行时评估操作。 在此过程中,dynamic 类型的变量会编译为 object 类型的变量。 因此,dynamic 类型只在编译时存在,在运行时则不存在。

优点就是dynamic像大部分脚本语言一样是动态变量,一方面方便某些动态使用,另一方面也可以和脚本语言对接。

但是缺点也很明显,可能有时使用它写的程序想要debug就没那么简单了。同时也别把dynamic作为函数的参数或者返回值,它只会比object带来更麻烦的后果。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/23787.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Grafana集成prometheus(4.Grafana添加预警)

上文已经完成了grafana对prometheus的集成及数据导入&#xff0c;本文主要记录grafana的预警功能&#xff08;以内存为例&#xff09; 添加预警 添加入口&#xff08;2个&#xff09; databorard面板点击edit&#xff0c;下方有个Alert的tab&#xff0c;创建Alert rules依赖…

ffmpeg + nginx 实现rtsp视频流转m3u8视频流,转码推流(linux)

FFmpeg即是一款音视频编解码工具&#xff0c;同时也是一组音视频编码开发套件&#xff0c;作为编码开发套件&#xff0c;它为开发者提供了丰富的音视频处理的调用接口。 FFmpeg提供了多种媒体格式的封装和解封装&#xff0c;包括多种音视频编码、多种协议的流媒体、多种多彩格式…

Redis常见面试题

Redis面试题 1、什么是 Redis? Redis 是完全开源免费的&#xff0c;遵守 BSD 协议&#xff0c;是一个高性能的 key-value 数据库。 Redis 与其他 key - value 缓存产品有以下三个特点&#xff1a; Redis 支持数据的持久化&#xff0c;可以将内存中的数据保存在磁盘中&#…

Linux内核网络参数调优命令

文章目录 ulimit -nsomaxconn Socket参数netdev_max_backlog参数tcp_max_syn_backlog参数 ulimit -n ulimit -n 是用于查看或设置当前用户进程打开文件描述符的最大值。文件描述符是操作系统用来访问文件或者其他I/O资源的一种抽象表示。在Linux和类Unix系统中&#xff0c;每个…

苹果电脑第三方应用程序卸载工具CleanMyMacX4.14

CleanMyMacX&#xff0c;这是一款可以帮助你快速识别并卸载电脑上的多个应用程序的第三方工具&#xff0c;省去了逐个卸载的繁琐步骤。 我们首先要到下载CleanMyMacX&#xff0c; CleanMyMac X全新版下载如下: https://wm.makeding.com/iclk/?zoneid49983 然后&#xff0c…

基于java数据结构学习网设计与实现

摘 要 随着计算机信息化时代的来临&#xff0c;教育的信息化发展也日新月异。特别是高等院校对于教育信息化的重视程度越来越高&#xff0c;教育信息化必须要投入大量的时间精力去开发一套管理系统&#xff0c;本文论述了信息化的管理手段在日常教学、考试过程中的重要性和必要…

【项目 计网2】4.4网络模型 4.5协议 4.6网络通信的过程

文章目录 4.4网络模型OSI七层参考模型TCP/IP四层模型&#xff08;常用&#xff09;简介四层介绍 4.5协议简介常见协议UDP协议TCP协议IP协议以太网帧协议&#xff08;MAC地址封装&#xff09;ARP协议&#xff08;IP->MAC&#xff09; 4.6网络通信的过程封装分用 4.4网络模型 …

1新手篇:熟悉 NestJS

前言 经过了需求分析以及技术选型之后,我们正式步入了第三个环节:脚手架搭建。 工欲善其事,必先利其器,NestJS 为开发者提供了很多开箱即用的功能,我们可以根据团队的需求搭建一套适配所有业务开发的基础脚手架。因此,接下来的 2 章是基础篇的教学,我将带领大家逐步学…

Stable Diffusion 硬核生存指南:WebUI 中的 GFPGAN

本篇文章聊聊 Stable Diffusion WebUI 中的核心组件&#xff0c;强壮的人脸图像面部画面修复模型 GFPGAN 相关的事情。 写在前面 本篇文章的主角是开源项目 TencentARC/GFPGAN&#xff0c;和上一篇文章《Stable Diffusion 硬核生存指南&#xff1a;WebUI 中的 CodeFormer》提…

k8s存储卷

目录 一、为什么要存储卷&#xff1f;二、emptyDir存储卷三、hostPath存储卷四、 nfs共享存储卷五、PVC 和 PV5.1 PV和PVC之间的相互作用遵循的生命周期5.2 PV 的状态5.3 一个PV从创建到销毁的具体流程 六、静态创建pv和pvc资源由pod运用过程6.1 在NFS主机上创建共享目录&#…

03 制作Ubuntu启动盘

1 软碟通 我是用软碟通制作启动盘。安装软碟通时一定要把虚拟光驱给勾选上&#xff0c;其余两个可以看你心情。 2 镜像文件 我使用清华镜像网站找到的Ubuntu镜像文件。 Index of /ubuntu-releases/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror 请自己选择镜像…

cocosCreator 之 resources(一)

版本&#xff1a; v3.4.0 参考: resources资源加载 简介 项目中所有需要通过脚本动态加载的资源可以使用resources.load接口进行加载&#xff0c;且资源相关一定要放在assets\resources的目录或子目录下。 在assets\resources目录下的资源要注意&#xff1a; 如果只是依赖于…

8.1Jmeter5.1:Jmeter SSL

Jmeter配置证书请求双向认证SSL的web接口 需求:需要通过Jmeter配置证书请求双向认证SSL的web接口 提供的证书:P12格式 备注:Jmeter需要导入的证书是keystore证书 那么要先把P12转成keystore文件 1、使用p12生成keystore文件 keytool介绍 这里需要提到提到jdk自带的key…

K8S系列文章之 使用Kind部署K8S 并发布服务

简单介绍 kind 即 Kubernetes In Docker&#xff0c;顾名思义&#xff0c;就是将 k8s 所需要的所有组件&#xff0c;全部部署在一个docker容器中&#xff0c;是一套开箱即用的 k8s 环境搭建方案。使用 kind 搭建的集群无法在生产中使用&#xff0c;但是如果你只是想在本地简单…

网络安全 Day13-Linux三剑客awk知识

Linux三剑客awk知识 1. awk 介绍2. awk 语法3. 练习 1. awk 介绍 awk 是一门语言, 也是一个命令,Linux 有三剑客命令: grep/sed/awk三剑客的特长 grep 过滤内容sed 取行awk 取列 2. awk 语法 取列 取第一列文件($1): awk {print $1} 文件指定分隔符为文件: awk -F "指…

队列中offer,add;poll,remove;peek,element之间的区别

offer和add的区别 offer() 和 add() 都是向队列中加入新项。 一些队列有大小限制&#xff0c;因此如果想在一个满的队列中加入一个新项&#xff0c;多出的项就会被拒绝。 这时新的offer方法就可以起作用了。它不是对调用add()方法抛出一个unchecked异常&#xff0c;而只是得…

查看linux 所有运行的应用和端口命令

要查看 Linux 中所有运行的应用程序及其对应的端口&#xff0c;可以使用以下命令&#xff1a; 1. 使用 netstat 命令&#xff08;已被弃用&#xff0c;建议使用 ss 命令&#xff09;&#xff1a; netstat -tuln 这会显示当前系统上所有打开的网络连接和监听的端口。其中&#…

初阶C语言——特别详细地介绍函数

系列文章目录 第一章 “C“浒传——初识C语言&#xff08;更适合初学者体质哦&#xff01;&#xff09; 第二章 详细认识分支语句和循环语句以及他们的易错点 第三章 初阶C语言——特别详细地介绍函数 目录 系列文章目录 前言 一、函数是个什么鬼东西&#xff1f; 二、C语…

企业级Vue路由角色权限应该怎么做?

角色权限 角色权限&#xff0c;简单来说就是登录的用户能看到系统的哪些页面&#xff0c;不能看到系统的哪些页面。一般是后台管理系统才会涉及到如此复杂的角色权限。 对于 vue 技术栈&#xff0c;实现角色权限一般有两种方式。 第一种是利用 beforeEach 全局前置守卫。 第…

css中的bfc是什么?

什么bfc&#xff1f; BFC&#xff08;Block Formatting Context&#xff09;块级 格式化 上下文。 BFC就是页面上的一个隔离的独立盒子&#xff0c;容器里面的子元素和外面的元素不会相互影响。 为什么要bfc? bfc是我们去主动触发的,并不是自动就存在的,它是帮助我们解决cs…