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

这篇具有很好参考价值的文章主要介绍了【C#学习笔记】装箱和拆箱。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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


装箱和拆箱

在讲引用类型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 这两个变量之间的差异:

【C#学习笔记】装箱和拆箱,C#学习笔记,c#,学习,笔记
(从上图中可以看到,原本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 = 456
    The object-type value = 123
*/

拆箱

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

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

  • 将该值从实例复制到值类型变量中。
    【C#学习笔记】装箱和拆箱,C#学习笔记,c#,学习,笔记

从拆箱过程可以看出,实际上我们拆箱的时候,首先判断拆箱项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带来更麻烦的后果。文章来源地址https://www.toymoban.com/news/detail-624478.html

到了这里,关于【C#学习笔记】装箱和拆箱的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 通过自动装箱和拆箱解释所定义基础数据类型和其对应封装类的区别

     在刷软中的时候涉及到了值传递和地址传递传参的区别,其中提到不管是将基础数据类型的变量传值给对象数据类型的变量还是反过来都属于值传递,究其原因就是期间发生了自动装箱和拆箱,所以特地去查了相关资料  以Intrger为例子   Integer i =520; 等价于 Integer i = Int

    2024年02月04日
    浏览(41)
  • C#学习系列之装箱、拆箱、自定义转化、重载运算符

    学习基础。 装箱:值类型转化为引用类型。方便统一操作和存储。 本质上就是在堆上创建了引用类型的副本,新创建的引用类型和值类型(栈中)相互独立。 拆箱:显式转化。 显式或隐式转化,变成预期类型。 隐式转换语法 显式转化语法 利用现有的某种运算符,针对自定

    2024年02月05日
    浏览(37)
  • C# 一看就懂的装箱拆箱案例

    在C#中,装箱(Boxing)和拆箱(Unboxing)是值类型与引用类型之间相互转换的过程。 当一个值类型(如整数、结构体或枚举等)需要转换为对象(System.Object)或接口类型时,系统会自动创建一个新的对象实例,并将该值类型变量的值复制到新创建的对象中。这个过程就称为装

    2024年02月02日
    浏览(40)
  • Java中的自动装箱与自动拆箱

    在Java中,基本数据类型与其对应的封装类之间可以进行自动转换,这种特性称为自动装箱(autoboxing)和自动拆箱(unboxing)。自动装箱和自动拆箱使得我们在使用基本数据类型时更加方便,同时也提高了代码的可读性和健壮性。本文将详细介绍Java中的自动装箱和自动拆箱机

    2023年04月22日
    浏览(34)
  • Java 装箱拆箱原理 & 包装类型缓存池

    byte short int long float double boolean char 为了让上述基本数据类型可以转为对象,Java在1.5推出了一系列包装类,基本类和包装类互相转换的过程,称为装箱和拆箱 缓存池也叫常量池。它是事先存储一些常量数据用以提高性能节省空间的一种技术,大部分的包装类型都实现了缓存池

    2024年02月16日
    浏览(38)
  • 数据结构----基本封装、包装类、装箱与拆箱、泛型详解

    在Java中,由于基本类型不是继承自Object,为了在泛型代码中可以支持基本类型,Java给每个基本类型都对应了一个包装类型。 基本数据类型 包装类 byte Byte short Short int Integer long Long float Float double Double char Character boolean Boolean 除了 Integer 和 Character, 其余基本类型的包装类都是

    2024年01月23日
    浏览(44)
  • int和Integer的区别是什么,自动装箱和自动拆箱到底是什么?

    基本类型 vs. 包装类型: int 是Java的基本数据类型,它直接存储整数值,不具备任何方法或属性。基本类型通常更高效,占用更少的内存。 Integer 是 int 的包装类,它是一个对象,具有方法和属性。包装类提供了一些额外的功能,如转换为其他数据类型、比较、转换为字符串等

    2024年02月08日
    浏览(39)
  • 【Java基础教程】(三十二)常用类库篇 · 第二讲:包装类 Wrapper Class——概念及用途, 自动装箱与拆箱,常用操作方法~

    在Java编程中,包装类(Wrapper Class)是一种特殊的类,它们允许将基本数据类型包装(Wrap)成对象。每个原始数据类型都有对应的包装类,例如 Integer 对应整型, Double 对应浮点型等。包装类提供了一些有用的方法和功能,方便我们操作和处理与原始数据类型相关的数据。

    2024年02月15日
    浏览(57)
  • C# 学习笔记--个人学习使用 <2>

    什么是委托? 委托 Delegate 是函数指针的升级版 Delegate 的意思是,这有一件事情,我不亲自去做,而是交给别人去做,也就是间接地去做; 我们可以看到输出结果如下: 在这个例子里,是通过函数的名字,来调用,是直接调用 我们可以看到输出结果如下: 可以看到输出结果

    2024年02月11日
    浏览(55)
  • 【C#学习笔记】【GUID】

    第一章 【C#学习笔记】【StackTrace】 第二章 【C#学习笔记】【Attribute】 第三章 【C#学习笔记】【Interface】 第四章 【C#学习笔记】【GUID】 关于GUID的介绍。 GUID(全局同意标识符)是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的。 通常平台会提供生

    2023年04月08日
    浏览(34)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包