搞清楚C#中的值类型(基础类型)和引用类型

 

关于引用类型的值改变

值类型理解:变量的交换等于在一个新的地方按照连锁店的规范标准(统一店面理解为相同的变量内容)新开一个分店,这样新开的店与其它旧店互不相关、各自运营。
引用类型理解:变量的交换等于把现有一间店的钥匙(变量引用地址)复制一把给了另外一个老板,此时两个老板同时管理一间店,两个老板的行为都有可能对一间店的运营造成影响。
 
简单来说:值类型的值发生改变后,不会影响改变之前的值。举个例子:
function chainStore()
{
var store1='Nike China';
var store2=store1;
store1='Nike U.S.A.';//在这里store1的值改变了,但下面输出的时候仍然是没改变之前的值.
alert(store2); //Nike China
}
chainStore();
//把一个值类型(也可以叫基本类型)store2传递给另一个变量(赋值)时,其实是分配了一块新的内存空间,因此改变store1的值对store2没有任何影响,因为它不像引用类型,变量的交换其实是交换了指像同一个内容的地址。
 
而引用类型的值发生改变后,只是改变前的变量名和改变后的变量名(即使前后变量名没有发生改变)在堆中的引用地址不一样了,而这两个地址指向的是栈中的同一个数据。所以说引用类型变量修改后,其指栈中的数据也改变了。
function chainStore()
{
var store1=['Nike China'];
var store2=store1;
alert(store2[0]); //Nike China
store1[0]='Nike U.S.A.';
alert(store2[0]); //Nike U.S.A.
}
chainStore();
 
//在上面的代码中,store2只进行了一次赋值,理论上它的值已定,但后面通过改写store1的值,发现store2的值也发生了改变,这正是引用类型的特征,也是我们要注意的地方。
--------------------------------------------------
 
 
 
引用类型与值类型相同的是,结构体也可以实现接口;引用类型可以派生出新的类型,而值类型不能;引用类型可以包含null值,值类型不能;引用类型变量的赋值只复制对象的引用,而不复制对象本身。而将一个值类型变量赋给另一个值类型变量时,将复制包含的值。
值类型及基本数据类型,常被分为四类八种:
            四类:整型,浮点型,字符型,逻辑型
            八种:1,整型3种 byte, short, int, long
                     2,浮点型 float, double
                     3,字符型 char
                     4,逻辑型 boolean

引用类型

       除了四类八种的基本类型,其他数据类型都是引用类型,例如:string类型,object类型等。

传递:

       值传递:基本数据类型复制都属于值传递。传递的是实实在在的变量值,是原参数的拷贝,实参传递给形参的值,形参发生变化而不影响实参。
       引用传递:引用类型之间的赋值属于引用传递。传递的对象是引用的地址,即将实参的地址传递给形参,形参改变了实参也相应发生变化,因为指向同一个地址。
 
存储:
引用类型在栈中存储一个引用,其实际的存储位置位于托管堆。为了方便,简称引用类型部署在托管堆上。值类型总是分配在它声明的地方,作为字段时,跟随其所属的变量(实例)存储;作为局部变量时,存储在栈上。  
1.    值类型:只需一段单独的内存,用于存储时间的数据。
          引用类型:需要两端内存:
                第一段:实际数据,总是位于堆中
                第二段:一个引用,指向数据在堆中的存储位置
          数据如果不是其他类型的成员就会像下图一样存贮,对于值类型数据放在栈中。对于引用类型数据放在堆中而引用放在栈中
    2.存储引用类型对象的成员
          引用类型对象的数据始终存储在堆中,如下图。
          值类型数据,或引用类型数据的引用可以放在堆里,也可以放在栈里。
例如:引用类型的对象名称为MyType,有两个成员:一个值类型成员和一个引用类型成员,将如何存储呢?
        对于一个引用类型,其实例的数据始终放在堆里。既然两个成员都是对象数据的一部分,那么他们都会被放在堆里,无论是值类型还是引用类型 如图:
    
装箱和拆箱
     装箱就是值类型转化为引用类型的过程。将一个值类型变量装箱成一个引用类型变量,首先会在托管堆上为新的引用类型变量分配内存空间,然后将值类型变量拷贝到托管堆上新分配的对象内存中,然后返回新分配的对象内存地址。  
例如:
int val = 100;
object obj = val;
Console.WriteLine (“对象的值 = {0}",obj);
 
     拆箱操作即是装箱的逆操作,拆箱的过程,是将值类型转换为引用类型,再由引用类型转换为值类型的过程。
 
为什么要装箱和拆箱?
 
装箱和拆箱的过程中涉及堆和栈的转换。直接影响性能,使用装拆箱是C#面向对象的精髓。处理大型的程序和软件,特别是有大批量数据的时候,这个很有必要的,比如代码片段中: 如果不使用装箱,就必须使用string[]strList=new string[5000000],这样就造成严重的性能问题。
 
(6)辨明值类型和引用类型的使用场合。
在C#中,我们用struct/class来声明一个类型为值类型/引用类型。考虑下面的例子: 如果SomeType是值类型,则只需要一次分配,大小为SomeType的100倍。而如果SomeType是引用类型,刚开始需要100次分配,分配后数组的各元素值为null,然后再初始化100个元素,结果总共需要进行101次分配。这将消耗更多的时间,造成更多的内存碎片。
所以,如果类型的职责主要是存储数据,值类型比较合适。一般来说,值类型(不支持多态)适合存储供C#应用程序操作的数据,而引用类型(支持多态)应该用于定义应用程序的行为。通常我们创建的引用类型总是多于值类型。如果满足下面情况,那么我们就应该创建为值类型:
该类型的主要职责用于数据存储。
该类型的共有接口完全由一些数据成员存取属性定义。
该类型永远不可能有子类。
该类型不具有多态行为。
 
 
posted @ 2020-06-07 16:45  gaoyang'Blog  阅读(285)  评论(0编辑  收藏  举报