zoukankan      html  css  js  c++  java
  • C#基础复习(2) 之 装箱拆箱

    参考资料

    [1] @只增笑耳Jason的回答 https://www.zhihu.com/question/57208269
    [2] 《C# 捷径教程》

    疑难解答

    1. 装箱和拆箱是什么?
    2. 何时发生装箱与拆箱?
    3. 装箱与拆箱的效率如何?

    装箱和拆箱是什么?

    在C#中,装箱和拆箱发生在值类型与引用类型之间。当我们把一个值类型转换成引用类型时,就发生了装箱操作。反之,当我们将一个引用类型转换成值类型时,就发生了拆箱操作。对于值类型和引用类型,感觉《C# 捷径教程》中讲的比较详尽(当然也可以参考我上一篇写的关于值类型与引用类型的文章):

    你用类来定义对象,用结构来定义值。二者之间存在一个清晰的界限。对象存活在有垃圾回收的内存堆上。值通常存活在临时的存储空间里,比如栈。前面提到过的一个显著的例外就是,如果值类型作为一个字段被包含在一个对象中,那它就可以存活在堆上。它不是自治的,GC不直接控制它的生命周期。

    对于装箱拆箱,知乎上有个答主感觉回答的很精辟:

    简单地讲装箱就是把一个放在stack上的值移动到heap上,拆箱正好相反

    如下代码展示了一个较为典型的装箱操作:

    1 class BoxAndUnBox{
    2 
    3     public static void Main(string[] args) {
    4         int a = 1;
    5 
    6         Print(a);   // =>在此处值类型a被装箱为object类型
    7     }
    8 
    9     static void Print(object obj) {
    10         Console.WriteLine(obj);
    11     }
    12 }
    

    在第6行处发生了装箱操作,变量a为值类型int,给a分配的空间在本地栈上。而Print方法接受一个对象引用,这个对象引用是一个指向基于堆的对象的引用。

    当我们将值类型传入该方法时,就发生了装箱操作,《C# 捷径教程》对此是这样描述的:

    CLR创建了一个运行时包装器类来包含这个值类型的副本。包装器类的实例存活在对上,通常称为装箱对象。这是CLR来联系值类型和引用类型之间间隔的方法。

    其中在第六行发生的操作如下图所示。

    被装箱后,关键的点是箱子内的值是初始值的副本,这意味着我们就算对箱子内的值进行更改,也不会影响到初始值(但并不总是这样,如果使用接口类型进行装箱,则修改原始值是可能的)。

    何时发生装箱与拆箱?

    根据《C# 捷径教程》所说,当以下任意转换发生时,值类型就被装箱。

    1. 从值类型转换成对对象引用
    2. 从值类型转换成System.ValueType引用
    3. 从值类型转换成指向值类型实现的接口的引用
    4. 从枚举类型转换成System.Enum引用

    第三种情况光看文字描述可能不太清楚,下面上代码。

    1 public interface IPrint {
    2     void Print();
    3 }
    4 
    5 public struct Value : IPrint {
    6 
    7     public int x;
    8 
    9     public Value(int x) {
    10         this.x = x;
    11     }
    12 
    13     public void Print() {
    14         Console.WriteLine(x);
    15     }
    16 }
    17 
    18 class BoxAndUnBox{
    19     public static void Main(string[] args) {
    20         Value value = new Value(3);
    21         IPrint print = value;
    22         print.Print();
    23     }
    24 }
    

    其中在第21行,值类型value被装箱为IPrint接口类型。这个过程是隐式的,事实上,如果我们直接通过value.Print()来调用方法,则不会发生装箱。

    对于拆箱操作,其发生时机恰好与装箱操作相反,当以下任意转换发生时,引用类型就被拆箱为值类型。

    1. 从引用类型强制转回到值类型
    2. 从System.ValueType类型转换成值类型
    3. 从指向值类型实现的接口的引用类型转换成值类型
    4. 从System.Enum引用类型转换成值类型

    装箱与拆箱的效率如何?

    先说结论,装箱是低效的,对于拆箱,CLR的拆箱操作本身并不是低效的,低效的根源在于C#通常把拆箱操作和一个对值复制的操作组合在一起。

    在C#中,大部分装箱操作都是隐式的,举个例子,当我们将一连串的整型(值类型)插入到ArrayList(非泛型版本)中去时,每插入一次,都是一次装箱操作。

    那么如何避免装箱拆箱呢?一个有效的方法是使用泛型,C#中有武装到牙齿的泛型,对于上述向列表插入的值的方法,完全可以使用List来进行替代。

  • 相关阅读:
    多线程:C#.NET中使用BackgroundWorker在模态对话框中显示进度条
    通过外接程序将Outlook邮件导出成Word文档
    [轉]FusionChartsFree参数说明
    MSIL学习资源
    FastCGI Error 2147467259 (0x80004005)
    编程实现双击某个文件用指定程序打开
    Excel api Enumerations 常量
    [轉]全面认识页面设置之PageSetup 对象
    AjaxFileUploaderV2.1增加可上传多个文件
    [轉]VB.NET and C# Comparison
  • 原文地址:https://www.cnblogs.com/sword-magical-blog/p/10539456.html
Copyright © 2011-2022 走看看