值类型和引用类型的区别
- 格式:doc
- 大小:51.50 KB
- 文档页数:5
C#基础:值类型和引⽤类型的区别⼀、值类型和引⽤类型的区别.NET的类型可以分为两类:值类型和引⽤类型。
这两种类型各有特点,即使它们都继承⾃System.Object,并且有装箱和拆箱等操作确保两种类型可以⽅便地交互,但是理解值类型和引⽤类型将有助于程序员编写出⾼效的代码,相反的,在不理解值类型和引⽤类型的情况下,程序员很容易编写出可以正确执⾏但性能较差的代码。
所有.NET的类型都可以分为两类:值类型和引⽤类型。
最简单也最明确的⼀个区分标准是:所有的值类型都继承⾃System.ValueType(System.ValueType继承⾃System.Object),也就是说,所有继承⾃System.ValueType的类型都是值类型,⽽其他类型都是引⽤类型。
常⽤的值类型包括结构、枚举、整数型、浮点型、布尔型等,⽽在C#中所有以class关键字定义的类型都是引⽤类型。
1、赋值时的区别引⽤类型和值类型最显著的⼀个区别在于变量的赋值问题。
值类型的变量将直接获得⼀个真实的数据副本,⽽对引⽤类型的赋值仅仅是把对象的引⽤赋给变量,这样就可能导致多个变量引⽤到⼀个实际对象实例上。
来看下⾯⼀个简单的⽰例:⾸先为了测试建⽴⼀个简单的引⽤类型和⼀个简单的值类型。
然后在Main⽅法中,测试对值类型和引⽤类型对象进⾏赋值的不同结果,代码如下:using System;namespace ConsoleApp1{///<summary>///⼀个简单的引⽤类型///</summary>public class Ref{public int iValue { get; set; }public Ref(int i){iValue = i;}public override string ToString(){return $"iValue的值为:{iValue.ToString()}";}}///<summary>///⼀个简单的值类型///</summary>public struct Val{public int Value { get; set; }public Val(int i){Value = i;}public override string ToString(){return $"Value的值为:{Value.ToString()}";}}class Program{static void Main(string[] args){// 测试引⽤类型的赋值Ref ref1 = new Ref(1);Ref ref2 = ref1;// 赋值ref2.iValue = 2;// 测试值类型的赋值Val val1 = new Val(1);Val val2 = val1;val2.Value = 2;//输出Console.WriteLine($"ref1:{ref1}");Console.WriteLine($"ref2:{ref2}");Console.WriteLine($"val1:{val1}");Console.WriteLine($"val2:{val2}");Console.ReadKey();}}}简单分析上⾯的代码,程序定义了⼀个引⽤类型Ref和⼀个值类型Val,两者的内容⼏乎完全相同。
解析:CLR支持两种类型:值类型和引用类型。
用Jeffrey Richter(《CLR via C#》作者)的话来说,“不理解引用类型和值类型区别的程序员将会把代码引入诡异的陷阱和诸多性能问题”。
这就要求我们正确理解和使用值类型和引用类型。
值类型包括C#的基本类型(用关键字int、char、float等来声明),结构(用struct关键字声明的类型),枚举(用enum关键字声明的类型);而引用类型包括类(用class关键字声明的类型)和委托(用delegate关键字声明的特殊类)。
C#中的每一种类型要么是值类型,要么是引用类型。
所以每个对象要么是值类型的实例,要么是引用类型的实例。
值类型的实例通常是在线程栈上分配的(静态分配),但是在某些情形下可以存储在堆中。
引用类型的对象总是在进程堆中分配(动态分配)。
(1)在C#中,变量是值还是引用仅取决于其基本数据类型。
C#的基本数据类型都与平台无关。
C#的预定义类型并没有内置于语言中,而是内置于.NET Framework中。
.NET使用通用类型系统(CTS)定义可以在中间语言(IL)中使用的预定义数据类型。
C#中所有的数据类型都是对象。
它们可以有方法、属性等。
例如,在C#中声明一个int变量时,声明实际上是CTS (通用类型系统)中System.Int32的一个实例:int i; i = 1; string s; s = i.ToString(); 下图说明了CTS中各个类型是如何相关的。
(2)System.Object和System.ValueType。
引用类型和值类型都继承自System.Object类。
不同的是,几乎所有的引用类型都直接从System.Object继承,而值类型则继承其子类,即直接继承System.ValueType。
作为所有类型的基类,System.Object提供了一组方法,这些方法在所有类型中都能找到。
其中包含toString 方法及clone等方法。
值类型和引⽤类型的区别c#⽀持两种类型:值类型(Value Types)和引⽤类型(Reference Types),值类型包括简单类型(如:char、int和float等等)、枚举类型(Enum Types)和结构类型(Struct Types)、引⽤类型包括类类型、接⼝类型、委托类型和数组类型。
值类型与引⽤类型的不同点在于:值类型的变量直接包含它们的数据,⽽引⽤类型的变量则把引⽤存储到对象中。
引⽤类型的两个变量可以引⽤同⼀个对象。
这样,对⼀个变量的操作就可能影响另⼀个变量所引⽤的对象。
值类型的每⼀个变量都具有它们⾃⼰的数据拷贝,因此对⼀个变量的操作不可能影响到另⼀个变量。
using System;using System.Collections.Generic;using System.Text;namespace ConsoleApplication1{class Person{public int Blood = 10;}class Program{public static void Add(int x){x += 10;Console.WriteLine("值类型当参数被传递并修改之后:"+x);}public static void Add(Person person){person.Blood += 10;Console.WriteLine("引⽤类型当参数被传递并修改之后:" + person.Blood);}static void Main(string[] args){//值类型变量int i = 10;Console.WriteLine("i的原值:" + i);Add(i);Console.WriteLine("但是i的值并没有因为函数的修改⽽修改:" + i);//引⽤类型变量Person person = new Person();Console.WriteLine("Blood的原值:" + person.Blood);Add(person);Console.WriteLine("但是Blood的值因为函数的修改⽽修改:" + person.Blood);//值类型和引⽤类型的区别,就在于当函数参数传递的时候.//值类型是把⾃⼰的值复制⼀份传递给别的函数操作.⽆论复制的值怎么被改变.其⾃⾝的值是不会改变的//⽽引⽤类型是把⾃⼰的内存地址传递给别的函数操作.操作的就是引⽤类型值的本⾝.所以值被函数改变了.//这就是传值和传址的区别Console.ReadLine();}}}using System;using System.Collections.Generic;using System.Text;namespace ConsoleApplication1{class Person{public int Blood = 10;}class Program{public static void Add(int x){x += 10;Console.WriteLine("值类型当参数被传递并修改之后:"+x);}public static void Add(Person person){person.Blood += 10;Console.WriteLine("引⽤类型当参数被传递并修改之后:" + person.Blood);}static void Main(string[] args){//值类型变量int i = 10;Console.WriteLine("i的原值:" + i);Add(i);Console.WriteLine("但是i的值并没有因为函数的修改⽽修改:" + i);//引⽤类型变量Person person = new Person();Console.WriteLine("Blood的原值:" + person.Blood);Add(person);Console.WriteLine("但是Blood的值因为函数的修改⽽修改:" + person.Blood);//值类型和引⽤类型的区别,就在于当函数参数传递的时候.//值类型是把⾃⼰的值复制⼀份传递给别的函数操作.⽆论复制的值怎么被改变.其⾃⾝的值是不会改变的//⽽引⽤类型是把⾃⼰的内存地址传递给别的函数操作.操作的就是引⽤类型值的本⾝.所以值被函数改变了.//这就是传值和传址的区别Console.ReadLine();}}}⼀个具有值类型(值类型(value type)的数据存放在栈内的⼀个变量中。
2018年必看的VR面试题各大公司、企业都在纷纷布局VR行业,而近期不断出现的VR影视、VR 教育、VR医疗,也让很多人都看到了VR市场的火爆,越来越多的人想从事这个前景一片大好的行业。
2018年必看的VR面试题在此分享给大家。
1.请简述值类型与引用类型的区别答:区别:1)值类型存储在内存栈中,引用类型数据存储在内存堆中,而内存单元中存放的是堆中存放的地址。
2)值类型存取快,引用类型存取慢。
3)值类型表示实际数据,引用类型表示指向存储在内存堆中的数据的指针和引用。
4)栈的内存是自动释放的,堆内存是.NET中会由GC来自动释放。
5)值类型继承自System.ValueType,引用类型继承自System.Object。
2.C#中所有引用类型的基类是什么答:引用类型的基类是System.Object值类型的基类是System.ValueType 同时,值类型也隐式继承自System.Object3.请简述ArrayList和List的主要区别答:ArrayList存在不安全类型‘(ArrayList会把所有插入其中的数据都当做Object来处理)装箱拆箱的操作(费时)List是接口,ArrayList是一个实现了该接口的类,可以被实例化。
4.请简述GC(垃圾回收)产生的原因,并描述如何避免?答:产生原因:GC回收堆上的内存避免:1)减少new产生对象的次数2)使用公用的对象(静态成员)3)将String换为StringBuilder5.请描述Interface与抽象类之间的不同答:抽象类表示该类中可能已经有一些方法的具体定义,但接口就是公公只能定义各个方法的界面,不能具体的实现代码在成员方法中。
类是子类用来继承的,当父类已经有实际功能的方法时该方法在子类中可以不必实现,直接引用父类的方法,子类也可以重写该父类的方法。
实现接口的时候必须要实现接口中所有的方法,不能遗漏任何一个。
8.请简述关键字Sealed用在类声明和函数声明时的作用答:类声明时加Sealed可防止其他类继承此类,在方法中声明则可防止派生类重写此方法。
js值类型和引⽤类型JavaScript值类型和引⽤类型有哪些(1)值类型:数值、布尔值、null、undefined。
(2)引⽤类型:对象、数组、函数。
四、如何理解值类型和引⽤类型及举例我们可以⽤“连锁店”和“连锁店钥匙”来理解,不知道以下⽐喻合不合适,^-^。
(1)值类型理解:变量的交换等于在⼀个新的地⽅按照连锁店的规范标准(统⼀店⾯理解为相同的变量内容)新开⼀个分店,这样新开的店与其它旧店互不相关、各⾃运营。
【值类型例⼦】复制代码代码如下:function chainStore(){var store1='Nike China';var store2=store1;store1='Nike U.S.A.';alert(store2); //Nike China}chainStore();//把⼀个值类型(也可以叫基本类型)store2传递给另⼀个变量(赋值)时,其实是分配了⼀块新的内存空间,因此改变store1的值对store2没有任何影响,因为它不像引⽤类型,变量的交换其实是交换了指像同⼀个内容的地址。
(2)引⽤类型理解:变量的交换等于把现有⼀间店的钥匙(变量引⽤地址)复制⼀把给了另外⼀个⽼板,此时两个⽼板同时管理⼀间店,两个⽼板的⾏为都有可能对⼀间店的运营造成影响。
【引⽤类型例⼦】复制代码代码如下:function chainStore(){var store1=['Nike China'];var store2=store1;alert(store2[0]); //Nike Chinastore1[0]='Nike U.S.A.';alert(store2[0]); //Nike U.S.A.}chainStore();//在上⾯的代码中,store2只进⾏了⼀次赋值,理论上它的值已定,但后⾯通过改写store1的值,发现store2的值也发⽣了改变,这正是引⽤类型的特征,也是我们要注意的地⽅。
c#基础系列之值类型和引⽤类型的深⼊理解前⾔不知不觉已经踏⼊坑已10余年之多,对于c#多多少少有⼀点⾃⼰的认识,写出来渴求同类抨击,对⾃⼰也算是个⼗年之痒的⼀个总结。
C#把数据类型分为值类型和引⽤类型1.1:从概念上来看,其区别是值类型直接存储值,⽽引⽤类型存储对值的引⽤。
1.2:这两种类型在内存的不同地⽅,值类型存储在堆栈中,⽽引⽤类型存储在托管对上。
存储位置的不同会有不同的影响。
下⾯话不多说了,来⼀起看看详细的介绍吧基本概念CLR⽀持两种类型:值类型和引⽤类型。
⾯试过很多5年左右的同学,有很多连值类型和引⽤类型的基本概念都回答不上来,难道现在的c#开发⼈员基础这么弱了吗?还是⼤家都不重视基础呢?这个随便找⼀篇博客都可以基础⼊门的。
引⽤类型哪些类型是引⽤类型呢?其实⼀个可以称为”类“的类型都是引⽤类型。
引⽤类型总是从托管堆上分配的,常⽤的语法就是New XX(). C#的new 操作符会返回对象的指针 - 也就是指向对象数据的内存地址的⼀个引⽤。
引⽤类型的传递其实传递的是对象的指针(string类型⽐较特殊),所以在特定的场景下性能是⾼于值类型的。
⼀个引⽤类型在创建时默认为null,也就是说当前变量不指向⼀个有效的对象,也就是我们常遇到的异常“未将对象引⽤设置到对象的实例”。
值类型因为引⽤类型变量都需要进⾏⼀次堆内存的分配,这会给GC造成很⼤的压⼒,所以CLR提供了轻量级类型“值类型”。
值类型⼀般在线程栈上分配。
(注意:值类型可以嵌⼊⼀个引⽤对象中)⼀个值类型变量其实就包含了值类型实例的值,所以它没有引⽤类型的指针(⼤家猜想值类型需不需要类型对象指针呢?)相同点和不同点相同点值类型和引⽤类型都是System.Object的⼦类值类型和引⽤类型都可以继承接⼝。
(很多⼈都认为值类型不能继承接⼝)interface Itest{void test();}struct TestStruct : Itest{public void test(){throw new NotImplementedException();}}不同点值类型分配在堆栈上,引⽤类型是在托管堆上分配的。
漫谈值类型和引用类型一.前言从这个简单程序的输出结果,你想到了什么?是不是与你心中想的结果不一致?是不是觉得输出的结果应该为:i is 1,o is 8,o2 is 8二.程序执行前图 2我们都知道,每一个方法在执行前,操作系统会给方法内每个变量分配内存空间。
从图2中就可以看出,在执行前各变量(i,o,o2)已分配了内存,且各自都有初始值。
从图中,可以发现变量i和变量o,o2有些许不同。
变量i在内存中存储的值和程序中的值是一样的,都是0;变量o,o2在内存中存储的值和程序中的值不一样,内存中存储的值是一个地址(0x00000000),程序中的值是null,那变量o,o2的null值存储在哪呢?为什么变量i和变量o,o2会有如此大的不同呢?我们都知道,C#有两大类型:值类类型和引用类型。
图2中int属于值类型,object属于引用类型。
接下来,介绍一下值类型和引用类型:1.值类型的值存储在内存栈上,引用类型的值存储在内存堆中。
园中有很多博文这么描述,我用程序验证了一下全局的值类型变量的值,静态的值类型变量的值,引用类型实例中值类型成员的值,如下图3图 3从图中,可以看出变量(j,o,seg,st)的值应该是在同一个存储区域中,而变量(gi)是在另外一个存储区域中。
引用类型Student的成员Age的地址还未分配。
所以说值类型的值存储在内存栈上是不准确的。
查找了一些资料,内存格局分为四个区:1)全局数据区:存放全局变量,静态变量,常量的值2)代码区:存放程序代码3)栈区:存放为运行而分配的局部变量,参数等4)堆区:自由存储区。
更为准确的说,方法体内的值类型变量的值存储在内存栈上,引用类型变量的值存储在内存堆上。
由于对象实例是引用类型变量的值,而对象实例成员只是对象实例的一部分,所以其随对象实例整个存储在内存堆上。
或许眼尖的园友发现了,上面那句话还是不对,结构体StructEg 的引用类型成员Name的数据就没有存储在内存栈上。
基本数据类型和引⽤类型的区别⼀、基本数据类型当把基本数据类型的变量赋值给另外⼀个变量时,修改其中⼀个变量的值,不会影响另外⼀个变量的值,看下⾯的例⼦:从截图中可以看出,修改了a的值,b的值不会变化。
基本数据类型可以理解为双胞胎,⼀个改变,另外⼀个不⼀定改变。
⼆、引⽤类型当把引⽤类型的变量赋值给另外⼀个引⽤类型变量时,修改其中⼀个变量的值,另外⼀个变量的值也会随之改变,看下⾯的例⼦:object类型的变量:因为两个变量都是指向内存中的同⼀块地址,所以其中⼀个修改另外⼀个也好随之改变,可以理解成⼈和⼈的影⼦。
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>基本数据类型和引⽤类型的区别</title><script>window.onload=function(){// 基本数据类型var a=10;var b=a; //b=10a=20;console.log(b);// 引⽤类型var arr1=[1,2,3,4,5];var arr2=arr1; //arr2=[1,2,3,4,5]arr2[1]=23;console.log(arr1);var obj1={name:"tom"};var obj2=obj1;="jack";console.log();};</script></head><body></body></html>。
彻底理解C#中值类型和引用类型的区别概念:1.值类型:数据存储在内存的堆栈中,从堆栈中可以快速地访问这些数据,因此,值类型表示实际的数据。
2.引用类型:表示指向存储在内存堆中的数据的指针或引用(包括类、接口、数组和字符串)。
区别:基本区别在于它们在内存中的存储方式。
值类型只将值存放在内存中,这些值类型都存储在堆栈中。
原始数据类型(如bool和int)都属于此类型。
而引用类型的内存单元中只存放内存堆中对象的地址,而对象本身放在内存堆中。
如果引用的值类型的值是null,则表示未引用任何对象。
下面我用两个例子说明它们的区别,这两个例子是我在VS2005开放环境中编译通过的:新建一个控制台应用程序:取名test1using System;using System.Collections.Generic;using System.Text;namespace Test1{class Program{static void Main(string[] args){int val = 100;Console.WriteLine("该变量的初始值为:{0}",val);Test(val);Console.WriteLine("该变量此时的值为:{0}",val);Console.Read();}static void Test(int getVal){int temp = 10;}}}命令提示符窗口得出的结果:为了说明问题,再新建一个控制台应用程序:取名test2 using System;using System.Collections.Generic;using System.Text;namespace test2{class DataTypeTest{public int val;}class Program{static void Main(string[] args){DataTypeTest bjTest = new DataTypeTest();Console.WriteLine("变量的初始值为:{0}",objTest.val);Test(objTest);Console.WriteLine("变量的值为:{0}",objTest.val);Console.Read();}static void Test(DataTypeTest dataTest)//这里传递的是对象地址{int temp = 10;dataTest.val = temp * 20;}}}命令提示符窗口得出的结果:总结:。
值类型和引用类型的区别是什么值类型和引用类型经常出现在JAVA等编程语言的书籍中,一些学习java的新手不是很懂这两者的区别,下面小编为大家介绍值类型和引用类型的区别,感兴趣的朋友们一起来看看吧!值类型和引用类型的区别一、定义值类型表示复制一个当前变量传给方法,当你在这个方法中改变这个变量的值时,最初生命的变量的值不会变。
引用类型表示你操作的数据是同一个,也就是说当你传一个参数给另一个方法时,你在另一个方法中改变这个变量的值,那么调用这个方法是传入的变量的值也将改变。
通俗说法: 值类型就是现金,要用直接用;引用类型是存折,要用还得先去银行取现。
----(摘自网上)值类型和引用类型的区别二、基本数据类型值类型有四类八种四类: 1,整型 2,浮点型 3,字符型 4,逻辑型八种: 1,整型3种 byte,short,int,long2,浮点型2种 float,double3,字符型1种 char4,逻辑型1种 boolean引用类型除了四类八种基本类型外,所有的类型都称为引用类型。
值类型和引用类型的区别三、值传递和引用传递值传递基本数据类型赋值都属于值传递,值传递传递的是实实在在的变量值,是传递原参数的拷贝,值传递后,实参传递给形参的值,形参发生改变而不影响实参。
引用传递引用类型之间赋值属于引用传递。
引用传递传递的是对象的引用地址,也就是它的本身(自己最通俗的理解)。
引用传递:传的是地址,就是将实参的地址传递给形参,形参改变了,实参当然被改变了,因为他们指向相同的地址。
引用和我们的指针差不多,但是它不又不需要我们去具体的操作。
值类型和引用类型的区别四、内存分配一个具有值类型(value type)的数据存放在栈内的一个变量中。
即是在栈中分配内存空间,直接存储所包含的值,其值就代表数据本身。
值类型的数据具有较快的存取速度。
一个具有引用类型(reference type)的数据并不驻留在栈中,而是存储于堆中。
.NET将数据类型分为值类型(value type)和引用类型(reference type)一个具有值类型(value type)的数据存放在栈内的一个变量中。
即是在栈中分配内存空间,直接存储所包含的值,其值就代表数据本身。
值类型的数据具有较快的存取速度。
一个具有引用类型(reference type)的数据并不驻留在栈中,而是存储于堆中。
即是在堆中分配内存空间,不直接存储所包含的值,而是指向所要存储的值,其值代表的是所指向的地址。
当访问一个具有引用类型的数据时,需要到栈中检查变量的内容,该变量引用堆中的一个实际数据。
引用类型的数据比值类型的数据具有更大的存储规模和较低的访问速度。
C#是一种类型安全的语言。
每一个变量都要求定义为一个特定的类型,并且要求存储在变量中的值只能是这种类型的值。
变量既能保存值类型,也可以保存引用类型,还可以是指针。
这一课将讲述前两种类型,关于指针的讨论我们将在下一课中进行。
下面是关于值类型和引用类型不同点的概论:如果一个变量v存储的是值类型,则它直接存储包含数据的对象。
任何其他的变量v’都不能直接存储已经由v存储了的对象,虽然v’可以存储一个和v存储的对象有着相同值的对象。
(译注:这意味着,v 和v’存储的对象毫不相干,任意改变其中一个存储的对象都不会影响到另一个变量存储的对象。
)如果变量v存储的是一个引用类型,它直接存储一个指向对象的引用,同时可能存在另一个变量v’,它也存储着指向这个对象的引用。
(译注:这意味着,改变v’引用的对象同时也会影响到v引用的对象,反之亦然。
)值类型在C#中你可以通过声明枚举类型或是结构类型来定义你自己的值类型。
C#以同样的方式处理用户自定义的类型和C#预定义的值类型,不过C#编译器可能更优于处理后者。
下面的表列出了C#中预定义的值类型的一些信息。
因为在C#中所有的基本值类型都是从object类型(最终基类)发展而来,所以下表中还显示了与这些预定义类型相对应的.Net框架中的System类型。
Java的值类型和引⽤类型⼀、问题描述 前⼏天因为⼀个需求出现了Bug。
说⾼级点也挺⾼级,说⽩点也很简单。
其实也就是⼀个很简单的Java基础⼊门时候的值类型和引⽤类型的区别。
只是开发的时候由于⾃⼰的问题,导致⼩问题的出现。
还好突然想起来以前看过⼀篇对于该问题讲解的博客,才能快速定位问题的位置。
防⽌下次再犯,顺便也就把这个当做笔记记录下来,放⼊⾃⼰的Bug集中。
⼆、值类型和引⽤类型的⽐较 这个⼤家应该都是没问题的,很简单。
值类型⽐较是⽐较值,引⽤类型是⽐较地址。
对于正常的操作来说,⽐较值类型我们可以直接使⽤== ,引⽤类型就使⽤equals 来做⽐较就不会出现问题。
引⽤类型1/**2 * 测试Integer3*/4public static void test_Integer(){5 Integer number_01 = 10;6 Integer number_02 = 10;7 System.out.println(number_01.equals(number_02));8 }上⾯的测试结果很明显是true,绝对没有问题的。
值类型1/**2 * 测试int3*/4public static void test_Int(){5int number_01 = 10;6int number_02 = 10;7 System.out.println(number_01 == number_02);8 }上⾯的测试结果很明显是true,绝对没有问题的。
三、问题但是问题就出现在,开发的使⽤为了防⽌出现为null的时候会被系统使⽤0来代替,所以就使⽤了Integer类型来做操作,并且在⽐较的时候⽤了== 。
这就很尴尬了,开始⾃测完全没出现问题,因为没到达记录数。
很开⼼,把代码提交下班,妥妥的。
但是尴尬的事情来了,测试报告出现在了邮箱⾥⾯了。
初始没问题的情况1/**2 * 测试Integer3*/4public static void test_Integer(){5 Integer number_001 = 10;6 Integer number_002 = 10;7 System.out.println(number_001 == number_002);8 }结果:当记录超过⼀定数的时候,出现问题1/**2 * 测试Integer3*/4public static void test_Integer(){5 Integer number_001 = 128;6 Integer number_002 = 128;7 System.out.println(number_001 == number_002);8 }结果:四、解决后⾯⼀想,很快确定问题了。
值类型和引⽤类型的区分1、简单⽐较 值类型的变量直接存储数据,⽽引⽤类型的变量持有的是数据的引⽤,数据存储在数据堆中。
值类型(value type):byte,short,int,long,float,double,decimal,char,bool 和struct 统称为值类型。
值类型变量声明后,不管是否已经赋值,编译器为其分配内存。
引⽤类型(reference type):string 和 class统称为引⽤类型。
当声明⼀个类时,只在栈中分配⼀⼩⽚内存⽤于容纳⼀个地址,⽽此时并没有为其分配堆上的内存空间。
当使⽤ new 创建⼀个类的实例时,分配堆上的空间,并把堆上空间的地址保存到栈上分配的⼩⽚空间中。
值类型的实例通常是在线程栈上分配的(静态分配),但是在某些情形下可以存储在堆中。
引⽤类型的对象总是在进程堆中分配(动态分配)。
2、相同点引⽤类型可以实现接⼝,值类型当中的结构体也可以实现接⼝;引⽤类型和值类型都继承⾃System.Object类。
3、不同点 1)范围⽅⾯C#的值类型包括:结构体(数值类型、bool型、⽤户定义的结构体),枚举,可空类型。
C#的引⽤类型包括:数组,⽤户定义的类、接⼝、委托,object,字符串。
2)内存分配⽅⾯数组的元素不管是引⽤类型还是值类型,都存储在托管堆上。
【注意:托管堆和进程堆,是⼀种么?另外:线程堆栈(Thread Stack)和托管堆(Managed Heap),⽇后会再单独涉及】引⽤类型在栈中存储⼀个引⽤,其实际的存储位置位于托管堆。
简称引⽤类型部署在托管推上。
⽽值类型总是分配在它声明的地⽅:作为字段时,跟随其所属的变量(实例)存储;作为局部变量时,存储在栈上。
(栈的内存是⾃动释放的,堆内存是.NET中会由GC来⾃动释放) 3)适⽤场合 值类型在内存管理⽅⾯具有更好的效率,并且不⽀持多态,适合⽤做存储数据的载体;引⽤类型⽀持多态,适合⽤于定义应⽤程序的⾏为。
你真的了解C#中的值和引⽤吗?(上)术语解释在阅读本⽂之前,你需要了解以下这⼏个术语是不同的:值、引⽤、值类型、引⽤类型。
C#中有三种值(value),分别是值类型实例的值、引⽤类型实例的值和引⽤。
值类型表达式的值是数据本⾝。
引⽤类型表达式的值是引⽤。
引⽤的值是⼀个数据块,指向引⽤类型的实例。
注意,上⾯我说的都是值类型表达式和引⽤类型表达式,包括局部变量和成员(如字段、属性、索引器)等。
现在,我们来考虑以下问题:值类型总是存储在栈上吗?值类型的局部变量总是存储在栈上吗?值类型要么存储在栈上,要么存储在堆上,是这样吗?引⽤的值是引⽤类型实例所在的地址吗?对于上⾯这些问题,您的答案是什么呢?误区:值类型到底存储在哪?在谈到值类型和引⽤类型的区别时,很多初学者常说值类型分配在⽅法的调⽤栈(或线程栈)上,引⽤类型分配在托管堆上,这种说法是错误的,⾄少前半部分是错误的。
实际上这根本不应该成为值类型和引⽤类型区别的答案,这是所答⾮所问。
值类型和引⽤类型的区别在语义层⾯,与存储位置⽆关,并不是值类型和引⽤类型不同的分配⽅式导致了它们⾏为上的差异,⽽是因为值和引⽤这两种类型在语义上的差异,才导致了他们不同的分配⽅式。
本⽂只讨论存储位置,不会深⼊介绍它们的区别。
有些朋友可能会说,详细的分配⽅式应该是这样的:引⽤类型的实例总是分配在托管堆上(在栈上⾄只保留实例的引⽤)值类型的实例总是分配在它声明的地⽅(声明为局部变量时被分配在栈上,声明为引⽤类型成员时则被分配在托管堆上)具体来讲也就是说,当值类型作为引⽤类型的私有字段时,它将作为引⽤类型实例的⼀部分,也分配在托管堆上。
⽽当引⽤类型作为值类型的成员变量时,栈上将保留该成员的引⽤,其实际数据还是保存在堆中。
在C# 2出现之前,这样的说法没有问题。
但C# 2引⼊了匿名⽅法和迭代器块后,以上说法就过于笼统了,它只看到了代码层⾯的东西,⽽没有看到编译器层⾯的东西。
值类型实例作为局部变量不都是分配在栈上。
Swift⾥的值类型与引⽤类型区别和使⽤Swift⾥⾯的类型分为两种:●值类型(Value Types):每个实例都保留了⼀分独有的数据拷贝,⼀般以结构体(struct)、枚举(enum)或者元组(tuple)的形式出现。
●引⽤类型(Reference Type):每个实例共享同⼀份数据来源,⼀般以类(class)的形式出现。
在这篇博⽂⾥⾯,我们会介绍两种类型各⾃的优点,以及应该怎么选择使⽤。
值类型与引⽤类型的区别值类型和引⽤类型最基本的分别在复制之后的结果。
当⼀个值类型被复制的时候,相当于创造了⼀个完全独⽴的实例,这个实例保有属于⾃⼰的独有数据,数据不会受到其他实例的数据变化影响:复制代码代码如下:// 下⾯是⼀个值类型的例⼦struct S { var data: Int = -1 }var a = S()var b = a // b是a的拷贝a.data = 42 // 更改a的数据,b的不受影响println("\(a.data), \(b.data)") // 输出结果 "42, -1"值类型就好像⾝份证复印件⼀样,复印出来之后,修改原件上⾯的内容,复印件上的内容不会变。
另⼀⽅⾯,复制⼀个引⽤类型的时候,实际上是默默地创造了⼀个共享的实例分⾝,两者是共⽤⼀套数据。
因此修改其中任何⼀个实例的数据,也会影响到另外那个。
复制代码代码如下:// 下⾯是⼀个引⽤类型的例⼦class C { var data: Int = -1 }var x = C()var y = x // y是x的拷贝x.data = 42 // 更改x的数据,等于同时修改了yprintln("\(x.data), \(y.data)") // 输出结果 "42, 42"Mutation(修改)在安全中扮演的⾓⾊值类型较引⽤类型来说,会让你更容易在⼤量代码中理清状况。
值类型和引用类型的区别[转]似乎“值类型和引用类型的区别”是今年面试的流行趋势,我已然是连续三次(目前总共也就三次)面试第一个问题就遇到这个了,这是多大的概率啊,100%,哈哈,我该买彩票去!言归正传,咱还是先来探讨探讨这二者之间有什么区别吧。
记得有一次电话面试中,我直接跟面试官说:“值类型是现金,引用类型是存折”,后来想想当时说这话虽是有点儿冲动地脱口而出,但也没什么不妥。
我这人不善于背理论的教条,喜欢把书本上那些生硬的话跟现实生活中常见的事物联系起来理解和记忆。
直白点儿说:值类型就是现金,要用直接用;引用类型是存折,要用还得先去银行取现。
声明一个值类型变量,编译器会在栈上分配一个空间,这个空间对应着该值类型变量,空间里存储的就是该变量的值。
引用类型的实例分配在堆上,新建一个引用类型实例,得到的变量值对应的是该实例的内存分配地址,这就像您的银行账号一样。
具体哪些类型是值类型哪些是引用类型,大家翻翻书,背一背就好了,不过我想,做过一段时间的开发,即使您背不了书上教条的定义,也不会把值类型和引用类型搞混的。
接下来,还是老规矩,咱看码说话吧。
1:public class Person2: {3:public string Name { get; set; }4:public int Age { get; set; }5: }6:7:public static class ReferenceAndValue8: {9:public static void Demonstration()10:{11:Person zerocool = new Person { Name = "ZeroCool", Age = 25 };12:Person anders = new Person { Name = "Anders", Age = 47 };13:14:int age = zerocool.Age;15:zerocool.Age = 22;16:17:Person guru = anders;18: = "Anders Hejlsberg";19:20:Console.WriteLine("zerocool's age:\t{0}", zerocool.Age);21:Console.WriteLine("age's value:\t{0}", age);22:Console.WriteLine("anders' name:\t{0}", );23:Console.WriteLine("guru' name:\t{0}", );24:}25: }上面这段代码,我们首先创建了一个Person类,包含了Name和Age两个属性,毋庸置疑,Person 类是引用类型,Name也是,因为它是string类型的(但string是很特殊的引用类型,后面将专门有一篇文章来讨论),但Age则是值类型。
值类型和引用类型的区别[转]似乎“值类型和引用类型的区别”是今年面试的流行趋势,我已然是连续三次(目前总共也就三次)面试第一个问题就遇到这个了,这是多大的概率啊,100%,哈哈,我该买彩票去!言归正传,咱还是先来探讨探讨这二者之间有什么区别吧。
记得有一次电话面试中,我直接跟面试官说:“值类型是现金,引用类型是存折”,后来想想当时说这话虽是有点儿冲动地脱口而出,但也没什么不妥。
我这人不善于背理论的教条,喜欢把书本上那些生硬的话跟现实生活中常见的事物联系起来理解和记忆。
直白点儿说:值类型就是现金,要用直接用;引用类型是存折,要用还得先去银行取现。
声明一个值类型变量,编译器会在栈上分配一个空间,这个空间对应着该值类型变量,空间里存储的就是该变量的值。
引用类型的实例分配在堆上,新建一个引用类型实例,得到的变量值对应的是该实例的内存分配地址,这就像您的银行账号一样。
具体哪些类型是值类型哪些是引用类型,大家翻翻书,背一背就好了,不过我想,做过一段时间的开发,即使您背不了书上教条的定义,也不会把值类型和引用类型搞混的。
接下来,还是老规矩,咱看码说话吧。
1:public class Person2: {3:public string Name { get; set; }4:public int Age { get; set; }5: }6:7:public static class ReferenceAndValue8: {9:public static void Demonstration()10: {11: Person zerocool = new Person { Name = "ZeroCool", Age = 25 };12: Person anders = new Person { Name = "Anders", Age = 47 };13:14:int age = zerocool.Age;15: zerocool.Age = 22;16:17: Person guru = anders;18: = "Anders Hejlsberg";19:20: Console.WriteLine("zerocool's age:\t{0}", zerocool.Age);21: Console.WriteLine("age's value:\t{0}", age);22: Console.WriteLine("anders' name:\t{0}", );23: Console.WriteLine("guru' name:\t{0}", );24: }25: }上面这段代码,我们首先创建了一个Person类,包含了Name和Age两个属性,毋庸置疑,Person 类是引用类型,Name也是,因为它是string类型的(但string是很特殊的引用类型,后面将专门有一篇文章来讨论),但Age则是值类型。
接下来我们来看看Demonstration方法,其中演示的就是值类型跟引用类型的区别。
首先,我们声明了两个Person类的实例对象,zerocool和anders,前面提到过,这两个对象都被分配在堆上,而zerocool和anders本身其实只是对象所在内存区域的起始地址引用,换句话说就是指向这里的指针。
我们声明对象实例时也顺便分别进行了初始化,首先我们看,zerocool 对象的值类型成员,我们赋值为25(对,我今年25岁),anders(待会儿你们就知道是谁了)的Name属性,我们赋值为“Anders”。
齐活儿,接下来看我们怎么干吧。
我们声明一个值类型变量age,直接在初始化时把zerocool的Age值赋给它,显然,age的值就是25了。
但这个时候zerocool不高兴了,他想装嫩,私自把自己的年龄改成22岁,刚够法定结婚年龄。
然后我们又声明了一个引用类型的guy对象,初始化时就把anders赋给它,然后anders露出庐山真面目了,他的名字叫“Anders Hejlsberg”(在此向C#之父致敬)。
接下来我们来分别答应出这几个变量的值,看看有什么差别。
你可能要觉得奇怪(你要不觉得奇怪,也就不用再接着往下看了),为什么我们改了zerocool.Age 的值,age没跟着变,改了的值,却跟着变了呢?这就是值类型和引用类型的区别。
我们声明age值类型变量,并将zerocool.Age赋给它,编译器在栈上分配了一块空间,然后把zerocool.Age的值填进去,仅此而已,二者并无任何牵连,就像复印机一样,只是把zerocool.Age的值拷贝给age了。
而引用类型不一样,我们在声明guy的时候把anders 赋给它,前面说过,引用类型包含的是只想堆上数据区域地址的引用,其实就是把anders的引用也赋给guy了,因此这二者从此指向了同一块内存区域,既然是指向同一块区域,那么甭管谁动了里面的“奶酪”,另一个变现出来的结果也会跟着变,就像信用卡跟亲情卡一样,用亲情卡取了钱,与之关联的信用卡账上也会跟着发生变化。
一提到钱,估计大家伙儿印象就深了些吧,呵呵!另外,性能上也会有区别的。
既然一个是直接操作内存,另一个则多一步先解析引用地址,那么显然很多时候值类型会减小系统性能开销。
但“很多时候”不代表“所有时候”,有些时候还得量力而为,例如需要大量进行函数参数传递或返回的时候,老是这样进行字段拷贝,其实反而会降低应用程序性能。
另外,如果实例会被频繁地用于Hashtable或者ArrayList之类的集合中,这些类会对其中的值类型变量进行装箱操作,这也会导致额外的内存分配和内存拷贝操作,从应用程序性能方面来看,其实也不划算。
哦对了,上面提到了一个概念,装箱。
那么什么是装箱呢?其实装箱就是值类型到引用类型的转化过程。
将一个值类型变量装箱成一个引用类型变量,首先会在托管堆上为新的引用类型变量分配内存空间,然后将值类型变量拷贝到托管堆上新分配的对象内存中,最后返回新分配的对象内存地址。
装箱操作是可逆的,所以还有拆箱操作。
拆箱操作获取只想对象中包含值类型部分的指针,然后由程序员手动将其对应的值拷贝给值类型变量。
接下来我们来看看典型的装箱和拆箱操作。
1:public static class BoxingAndUnboxing2: {3:public static void Demonstration()4: {5:int ageInt = new int();6:7:// Boxing operation.8:object ageObject = ageInt;9:10://ageObject = null;11:12:// Unboxing operation.13: ageInt = (int)ageObject;14:15: Console.WriteLine(ageInt);16: }17: }在该方法中,我们首先声明了一个值类型变量ageInt,但并未给它赋值,接着声明了一个典型的引用类型变量ageObject,并把ageInt赋给它,这里就进行了一次装箱操作。
编译器现在托管堆上分配一块内存空间(空间大小为对象中包含的值类型变量所占空间总和外加一个方法表指针和一个SyncBlockIndex),然后把ageInt拷贝到这个空间中,再返回该空间的引用地址。
接下来第13行则是拆箱操作,编译器获取到ageObject对象中值类型变量的指针,然后将其值拷贝给值类型变量。
如果你把第10行注释掉的代码打开(这是通俗说法,其实就是取消注释),那么第13行就会抛出System.NullReferenceException异常,要说问什么,这又会牵扯出值类型跟引用类型另一个大的不同。
看见了吧,声明ageInt时并没有赋值,如果关掉第10行代码,程序不会报错,最后打印出个0,这说明在声明值类型变量时,如果没有初始化赋值,编译器会自动将其赋值为0,既然值类型没有引用,那么它就不可能为空。
引用类型不一样,它可以为空引用,一张过期作废的银行卡是可以存在。
而如果将一个空的对象拆箱,编译器上哪儿去找它里面的值类型变量的指针呢?所以这也是拆箱操作需要注意的地方。
最后,我们在把值类型和引用类型之间其它一些明显区别大致罗列如下,以便大家能顺利通过面试第一问。
∙所有值类型都继承自System.ValueType,但是ValueType没有附加System.Object包含之外其它任何方法,不过它倒是改写了Equals和GetHashCode两个方法。
引用类型变量的Equals比较的是二者的引用地址而不是内部的值,值类型变量的Equals方法比较的是二者的值而不是……哦对了,值类型压根儿没有引用地址;∙值类型不能作为其它任何类型的基类型,因此不能向值类型中增加任何新的虚方法,更不该有任何抽象方法,所有的方法都是sealed的(不可重写);∙未装箱的值类型分配在栈上而不是堆上,而栈又不是GC的地盘儿,因此GC根本不过问值类型变量的死活,一旦值类型变量的作用范围一过,它所占的内存空间就立即被回收掉,不劳GC亲自动手。
以上罗列的都是值类型和引用类型之间的主要区别,文码并茂,相信应该给你留下比较深刻的印象,虽不够深,但愿能起到抛砖引玉的作用。
如果去面SDE职位,估计这深度就差不多了,我这文章不是面向那些要去面Senior SDE甚至Dev Lead的正神,咱这儿庙小,嘿嘿!。