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()}";writeline输出数值变量
}
}
///<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,两者的内容⼏乎完全相同。在Main⽅法中,分别测试了引⽤类型和值类型的赋值。当代码把⼀个引⽤类型变量赋值给另⼀个引⽤变量:Ref ref2 = ref1时,实际上是把ref1的对象引⽤赋给了ref2,这样,两个引⽤变量实际指向了同⼀个对象。如图所⽰:
⽽值类型的赋值则不同,val1和val2都保留了属于⾃⼰的数据副本,所以当val2改变时,val1不受到影响。如图所⽰:
上⾯代码的输出结果:
2、内存分配的区别
除了赋值的区别,引⽤类型和值类型在内存的分配位置上也有区别。引⽤类型的对象将会在堆上分配内存,⽽值类型的对象则会在堆栈上分配内存。堆栈的空间相对有限,但运⾏效率却⽐⾼的多。
3、来⾃继承结构的区别
最后,由于所有的值类型都有⼀个共同的基类:System.ValueType,所以值类型拥有⼀些引⽤类型不具有的共同性质,较重要的⼀点是值类型的⽐较⽅法:Equals⽅法的实现有了改变。所有的值类型都实现了内容的⽐较,⽽引⽤类型在没有重写Equals⽅法的情况下,仍然采⽤引⽤⽐较。还是以上⾯的代码为了,看下⾯的代码:
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}");
/
/ 测试引⽤类型的赋值
Ref ref1 = new Ref(1);
Ref ref2 = new Ref(1);
// 测试值类型的赋值
Val val1 = new Val(1);
Val val2 = new Val(1);
Console.WriteLine(ref1.Equals(ref2));
Console.WriteLine(val1.Equals(val2));
Console.ReadKey();
}
}
}
程序输出结果:
在Main⽅法中,分别定义了⼀对内容完全相同的值类型对象和引⽤类型对象,调⽤Equals⽅法来⽐较,发现值类型对象⽐较返回true,⽽引⽤类型对象⽐较返回false。
⼆、总结
所有继承⾃System.ValueType的类型都是值类型,⽽其他类型都是引⽤类型。值类型的赋值会产⽣⼀个新的数据副本,所以每个值类型都拥有⼀个数据副本。⽽引⽤类型的赋值则是赋值引⽤。值类型的对象分配在堆栈上,⽽引⽤类型的对象分配在堆上。当⽐较两个值类型时,进⾏的是内容⽐较。⽽⽐较两个引⽤类型时,进⾏的是引⽤⽐较。
上⾯列举的仅仅是值类型和引⽤类型的⼀些主要区别,通过这些本质区别,可以产⽣更多的细节区别,有兴趣的话可以⾃⾏研究。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论