C#与.NET程序员面试宝典 3.1.4 面试题23:有几种方法可以判断值类型和引用类型
值类型和引用类型都是.NET预定义的数据类型,虽然它们都继承自System.Object,但这两种类型各有特点。这道面试题在技术笔试中,是出现概率最高的面试题之一,读者一定要深入理解值类型和引用类型。
【出现频率】★★★★★
【关键考点】
值类型和引用类型的基本概念
值类型和引用类型的赋值
值类型和引用类型的继承结构
值类型和引用类型的内存分配
【考题分析】
.NET的类型可以分为值类型和引用类型。值类型(Value Type)实例通常分配在线程的堆栈上,并且不包含任何指向实例数据的指针。引用类型(Reference Type)类型实例分配在托管堆上,变量保存了实例数据的内存引用。有以下3种方法可以判断值类型和引用类型:
(1)值类型和引用类型赋值区别
值类型变量赋值只是进行数据复制,创建一个同值新对象,而引用类型变量的赋值仅仅是把对象的引用的指针赋值给变量,使得变量引用与对象共享同一内存地址。
下面来看一段简单的示例,首先为了演示的方便在这只建立一个引用类型和一个值类型。
using System;
namespace MyConsole
{
class ValueTypeAndRefType
{
class SomeRef //定义一个引用类型
{
public int x;
}
struct SomeVal //定义一个值类型
{
public int x;
}
static void Main(string[] args)
{
SomeRef Reference1 = new SomeRef(); //分配在托管堆上
SomeVal Value1 = new SomeVal(); //分配在堆栈上
Reference1.x = 3; //解析指针
Value1.x = 3; //在堆栈上修改
Console.WriteLine("引用类型: Reference1=" + Reference1.x); //显示为“3”
Console.WriteLine("值类型: Value1=" + Value1.x); //显示为“3”
SomeRef Reference2 = Reference1; //仅拷贝引用(指针)
SomeVal Value2 = Value1; //先在堆栈上分配,然后拷贝成员
Reference1.x = 8; //改变了Reference1.x和Reference2.x
Value1.x = 7; //改变了Value1.x,没有改变Value2.x
Console.WriteLine("引用类型: Reference1=" + Reference1.x); //输出信息
Console.WriteLine("引用类型: Reference2=" + Reference2.x);
Console.WriteLine("值类型: Value1=" + Value1.x);
Console.WriteLine("值类型: Value2=" + Value2.x);
Console.ReadLine();
}
}
}
(2)继承结构的区别
引用类型一般都有继承性。但由于值类型是密封的(sealed),因此值类型不能作为其他任何类型的基类,但是可以单继承或者多继承接口。另外一个区别是值类型都继承System.ValueType类,而引用类型则不会继承System.ValueType类。
(3) 内存分配的区别
值类型和引用类型在内存的分配位置上也有区别。值类型通常被分配在栈上,它的变量直接包含变量的实例,使用效率相对比较高。而引用类型分配在托管堆上,引用类型的变量通常包含一个指向实例的指针,变量通过该指针来引用实例。
注意:GC(垃圾回收,Gabage Collection)控制引用类型的内存回收,但不控制值类型的内存。在作用域结束时,值类型会自行释放,由于减少了托管堆的压力,因此在性能上具有一定的优势。
【答案】
值类型数据是在栈中分配空间的,是在数据定义的时候就按照所需要的大小分配空间,所以在数据的存取时效率比较高,而引用类型是在堆中分配的,类和结构都属于引用类型。通常程序员自己定义的数据类型大部分都是引用类型。当然最简单也最常用的一个方法是看这个类型是否继承自System.ValueType。继承自System.ValueType的类型是值类型反之则是引用类型。