【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.rhkb.cn/news/78787.html

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

相关文章

Huggingface使用

文章目录 前置安装Huggingface介绍NLP模块分类transformer流程模块使用详细讲解tokennizermodeldatasetsTrainer Huggingface使用网页直接体验API调用本地调用(pipline)本地调用&#xff08;非pipline&#xff09; 前置安装 anaconda安装 使用conda创建一个新环境并安装pytorc…

针对高可靠性和高性能优化的1200V碳化硅沟道MOSFET

目录 标题&#xff1a;1200V SiC Trench-MOSFET Optimized for High Reliability and High Performance摘要信息解释研究了什么文章创新点文章的研究方法文章的结论 标题&#xff1a;1200V SiC Trench-MOSFET Optimized for High Reliability and High Performance 摘要 本文详…

fishing之第二篇Gophish钓鱼平台搭建

文章目录 一、Gophish介绍二、Gophish部署三、Gophish配置0x01 功能介绍0x02 Sending Profiles(钓鱼邮箱发送配置)0x03 Email Templates(钓鱼邮件模板)0x04 Landing Pages(伪造钓鱼页面)0x05 Users & Groups(用户和组)0x06 Campaigns(钓鱼测试)0x07 Dashboard(仪…

ESP32-C2开发板 ESP8684芯片 兼容ESP32-C3开发

C2是一个芯片采用4毫米x 4毫米封装&#xff0c;与272 kB内存。它运行框架&#xff0c;例如ESP-Jumpstart和ESP造雨者&#xff0c;同时它也运行ESP-IDF。ESP-IDF是Espressif面向嵌入式物联网设备的开源实时操作系统&#xff0c;受到了全球用户的信赖。它由支持Espressif以及所有…

Markdown系列之Flowchat流程图

一.欢迎来到我的酒馆 介绍Markdown的Flowchart流程图语法。 目录 一.欢迎来到我的酒馆二.什么是Flowchart三.更进一步 二.什么是Flowchart 2.1 Flowchart是一款基于javascript的工具&#xff0c;使用它可以用代码创建简单的流程图。具体信息可以查看flowchart官网&#xff1a;…

百度秋招攻略,百度网申笔试面试详解

百度秋招简介 作为行业巨头&#xff0c;百度向社会提供的岗位一直都是非常吃香的&#xff0c;每年也都有很多考生密切关注&#xff0c;百度发布的招聘广告&#xff0c;以尽可能的让自己进入这家企业工作&#xff0c;实现自己的人生价值。那么百度每年的秋招时间是多久&#xf…

【JavaSE】面向对象编程思想之多态(图文详解)

目录 1. 多态的概念 2. 多态实现条件 3. 重写 4. 向上转型和向下转型 4.1 向上转型 4.2 向下转型 5. 多态的优缺点 6. 避免在构造方法中调用重写的方法 1. 多态的概念 多态的概念&#xff1a;通俗来说&#xff0c;就是多种形态&#xff0c;具体点就是去完成某个行为&a…

Linux学习笔记

Linux学习笔记 目录 一&#xff0e; 操作系统的发展历史与linux二&#xff0e; 安装VMWare三&#xff0e; 安装和配置CentOS 7四&#xff0e; Linux操作系统目录结构五&#xff0e; Linux命令 一&#xff0e; 操作系统的发展历史与linux 概述   操作系统产生与发展经历了人工…

VS2022程序集说明汉化

下载本地化的 .NET IntelliSense 文件 https://dotnet.microsoft.com/zh-cn/download/intellisense 目前本地化的 IntelliSense 文件不再可用。 可用的最新版本是 .NET 5。 建议使用英语 IntelliSense 文件。 .NET6的汉化需要自己动手&#xff1a; 教程可以参照下方&#xff1a…

机器人状态估计:robot_localization 功能包使用方法

机器人状态估计&#xff1a;robot_localization 功能包基本使用 前言功能包简介基本使用数据输入与数据输出坐标系设置性能参数调试 前言 移动机器人的状态估计需要用到很多传感器&#xff0c;因为对单一的传感器来讲&#xff0c;都存在各自的优缺点&#xff0c;所以需要一种多…

(文章复现)建筑集成光储系统规划运行综合优化方法matlab代码

参考文献&#xff1a; [1]陈柯蒙,肖曦,田培根等.一种建筑集成光储系统规划运行综合优化方法[J].中国电机工程学报,2023,43(13):5001-5012. 1.基本原理 本文建立的双层耦合模型内、外层分别对应求解容量配置与能量调度问题。外层模型设置光伏与储能容量备选集并将容量配置组合…

笛卡尔积文本的python处理

一 背景 大致背景是这样的&#xff0c;笔者在做数据处理时&#xff0c;遇到一个棘手的事情&#xff0c;主要遇到如下字符串拼接变动的场景&#xff0c;场景主要为&#xff0c;需要考虑如下两张表的组合&#xff1a; 表1-原始文本样式 序号文本样式1A变量B2A变量C3A变量CD4E变…

自然语言处理学习笔记(五)————切分算法

目录 1.切分算法 2.完全切分 3.正向最长匹配 4.逆向最长匹配 5.双向最长匹配 6.速度评测 1.切分算法 词典确定后&#xff0c;句子可能含有很多词典中的词语&#xff0c;他们有可能互相重叠&#xff0c;如何切分需要一些规则。常用规则为&#xff1a;正向匹配算法、逆向匹…

Redis事务、管道

一.Redis事务 1.概念 可以一次执行多个命令&#xff0c;本质是一组命令的集合。一个事务中的所有命令都会序列化&#xff0c;按顺序地串行化执行而不会被其它命令插入&#xff0c;不许加塞 2.Redis事务与数据库事物的区别 3.常用命令 4.事务执行情况 正常执行 即整个过程…

JMeter(二十四)、使用吞吐量控制器实现不同的用户操纵不同的业务

一、需求 需求&#xff1a;博客系统&#xff0c;模拟用户真实行为&#xff0c;80%的用户阅读文章&#xff0c;20%的用户创建文章&#xff0c;创建文章的用户随机的删除或者修改文章。 二、脚本实现 80%的用户查看文章 20%用户创建文章 根据post_id是否能整除2&#xff0c;决…

Python实现GA遗传算法优化BP神经网络回归模型(BP神经网络回归算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 遗传算法&#xff08;Genetic Algorithm&#xff0c;GA&#xff09;最早是由美国的 John holland于20世…

ChatGPT实战:创业咨询,少走弯路,少踩坑

用九死一生形容创业再适合不过&#xff0c;不过一旦成功回报也很诱人&#xff0c;这也是为什么那么多人下场创业。纸上得来终觉浅&#xff0c;绝知此事要躬行&#xff0c;创过业的人都知道其中的心酸&#xff0c;而他们也建议你去创业&#xff0c;因为那真不是一般人能干的事。…

Flutter编译一直显示Running Gradle task ‘assembleDebug‘

&#x1f525; 目前开发的Android Studio版本 &#x1f525; &#x1f525; 当前Flutter SDK 版本 &#x1f525; Flutter 3.10.6 • channel stable • https://github.com/flutter/flutter.git Framework • revision f468f3366c (3 周前) • 2023-07-12 15:19:05 -0700 Eng…

【云原生】Serverless 技术架构分析

一、什么是Serverless? 1、Serverless技术简介 ​ Serverless&#xff08;无服务器架构&#xff09;指的是由开发者实现的服务端逻辑运行在无状态的计算容器中&#xff0c;它由事件触发&#xff0c; 完全被第三方管理&#xff0c;其业务层面的状态则被开发者使用的数据库和存…

PVE虚拟化平台之安装openKylin开源操作系统

PVE虚拟化平台之安装openKylin开源操作系统 一、openKylin介绍1.1 openKylin简介1.2 openKylin特性 二、下载openKylin系统镜像2.1 官方网址2.2 下载openKylin系统镜像 三、上传镜像到PVE存储3.1 检查PVE环境3.2 上传镜像 四、创建虚拟机4.1 设置虚拟机名称4.2 操作系统设置4.3…