zoukankan      html  css  js  c++  java
  • 值类型与引用类型

    声明:部分类容转载自Hawkitc,地址:http://blog.csdn.net/qiaoquan3/article/details/51202926

    1、值类型变量与引用类型变量

      从概念上看,值类型直接存储其值,而引用类型存储对其值的引用。  

      声明一个值类型变量,编译器在内存的栈上分配一个空间,这个空间对应着该值类型变量,空间里存储的就是该变量的值。(注意:声明后不管是否已经赋值,编译器为其分配内存。)

      声明一个类时,只在栈中分配一小片内存用于容纳一个地址,而此时并没有为其分配堆上的内存空间。当使用 new 创建一个类的实例时,分配堆上的空间,并把堆上空间的地址保存到栈上分配的小片空间中。

      栈的内存是自动释放的,堆内存是.NET中会由GC来自动释放

    2、值类型与引用类型都有哪些?

    值类型:C#的所有值类型均隐式派生自System.ValueType:

    • 结构体:struct(直接派生于System.ValueType);
    • 数值类型:
    1. 整型:sbyte(System.SByte的别名),short(System.Int16),int(System.Int32),long(System.Int64),byte(System.Byte),ushort(System.UInt16),uint(System.UInt32),ulong(System.UInt64),char(System.Char);
    2. 浮点型:float(System.Single),double(System.Double);
    3. 用于财务计算的高精度decimal型:decimal(System.Decimal)。
    • bool型:bool(System.Boolean的别名);
    • 用户定义的结构体(派生于System.ValueType)。
    • 枚举:enum(派生于System.Enum);
    • 可空类型(派生于System.Nullable<T>泛型结构体,T?实际上是System.Nullable<T>的别名)。

    C#有以下一些引用类型:

    • 数组(派生于System.Array)
    • 用户用定义的以下类型:
    1. 类:class(派生于System.Object);
    2. 接口:interface(接口不是一个“东西”,所以不存在派生于何处的问题。Anders在《C# Programming Language》中说,接口只是表示一种约定[contract]);
    3. 委托:delegate(派生于System.Delegate);
    • object(System.Object的别名);
    • 字符串:string(System.String的别名)。

    3、值类型复制与引用类型复制

      值类型在复制的时候,传递的是这个值得本身。(即在栈上开辟一个内存,把值复制过去)
      引用类型在复制的时候,传递的是对这个对象的引用。(即在在栈上开辟一个内存,存放实际数据在堆中的地址,这个地址指向堆中的同一个对象)

     int n1 = 10;
     int n2 = n1;
          n2 = 20;
    Console.WriteLine("n1的值是{0},n2的值是{1}。",n1,n2);

    运行结果:n1的值是10,n2的值是20

    值类型传递在栈中的地址是不同的。

     Person p1 = new Person();
                p1.Name = "张三";
                Person p2 = p1;
                p2.Name = "李四";
                Console.WriteLine(p1.Name);

    运行结果:李四

    通过即时窗口不难看出为何改变p2的变量的值,会引起p1的变量值的变化。p1、p2指向的堆中的同一个地址 0x02391268

      注意:字符串string虽然也是引用类型,但是由于其的不可变性,每次赋值都是在堆上开辟新的空间,不会覆盖原来

    ***值得注意“对象赋值”

    为对象进行“赋值”的时候,情况却发生了变化,对一个对象进行操作时,实际上操作的是它的一个“引用”,或者称为“句柄”,所以假如“从一个对象到另一个对象”赋值,实际上就是将“引用”从一个地方复制到另一个地方。这意味着假若为对象使用“C=D”,那么C和D最终都会指向最初只有D才指向的那个对象。下面的例子将更加清晰的阐述这一点。
    下面的例子:
      

    class Number
            {
                private int temp;
                public int Temp
                {
                    get { return temp; }
                    set { temp = value; }
                }
            }
            static void Main(string[] args)
            {
                Number n1 = new Number();
                Number n2 = new Number();
                n1.Temp = 9;
                n2.Temp = 47;
     
                Console.WriteLine("1:n1.Temp:" + n1.Temp + ",n2.Temp:" + n2.Temp);
                n1 = n2;
                Console.WriteLine("1:n1.Temp:" + n1.Temp + ",n2.Temp:" + n2.Temp);
                n1.Temp = 27;
                Console.WriteLine("1:n1.Temp:" + n1.Temp + ",n2.Temp:" + n2.Temp);
                Console.ReadLine();
            }

    Number 类非常简单,它的两个实例(n1 和n2)是在main()里创建的。每个Number 中的i 值都赋予了一个不同的值。随后,将n2 赋给n1,而且n1 发生改变。在许多程序设计语言中,我们都希望n1 和n2 任何时候都相互独立。但由于我们已赋予了一个“引用”,所以下面才是真实的输出:
    1: n1.Temp: 9, n2.Temp: 47
    2: n1.Temp: 47, n2.Temp: 47
    3: n1.Temp: 27, n2.Temp: 27
    看来改变n1 的同时也改变了n2!这是由于无论n1 还是n2 都包含了相同的“引用”,它指向相同的对象(最初的“引用”位于n1 内部,指向容纳了值9 的一个对象。在赋值过程中,那个“引用”实际已经丢失;它的对象会由“垃圾收集器”自动清除)。这种特殊的现象通常也叫作“别名”,是.net操作对象的一种基本方式。但假若不愿意在这种情况下出现别名,又该怎么操作呢?可放弃赋值,并写入下述代码:
    n1.Temp = n2.Temp;
    这样便可保留两个独立的对象,而不是将n1 和n2 绑定到相同的对象。但您很快就会意识到,这样做会使对象内部的字段处理发生混乱,并与标准的面向对象设计准则相悖。

  • 相关阅读:
    散列函数的构造方法
    散列表(哈希表)查找
    散列表,哈希表,散列表查找法
    多路查找树之2-3-4树和B树
    多路查找树之2-3树的删除原理
    多路查找树之2-3树的插入原理
    多路查找树之2-3树
    字典:当索引不好用时2
    字典:当索引不好用时
    平衡二叉树的实现原理(代码实现)- 数据结构和算法78
  • 原文地址:https://www.cnblogs.com/apollo-shen/p/6804763.html
Copyright © 2011-2022 走看看