在 C# 中,数据类型大致可以分为两类,一类是值类型,一类是引用类型。初学者往往会被类型之间的相互赋值搞的很迷惑,尤其是引用类型变量的相互赋值。
在 C# 中,变量是值还是引用仅取决于其数据类型。C# 的基本数据类型都以平台无关的方式来定义,C# 的预定义类型并没有内置于语言中,而是内置于 .NET Framework 中。.NET 使用通用类型系统(CTS)定义了可以在中间语言(IL)中使用的预定义数据类型,所有面向.NET的语言都最终被编译为 IL,即编译为基于 CTS 类型的代码。
通用类型的系统的功能:
- 建立一个支持跨语言集成、类型安全和高性能代码执行的框架。
- 提供一个支持完整实现多种编程语言的面向对象的模型。
- 定义各语言必须遵守的规则,有助于确保用不同语言编写的对象能够交互作用。
例如,在 C# 中声明一个 int 变量时,声明的实际上是 CTS 中 System.Int32 的一个实例。这具有重要的意义:
- 确保 IL 上的强制类型安全;
- 实现了不同 .NET 语言的互操作性;
- 所有的数据类型都是对象。它们可以有方法,属性等。
C# 的所有值类型均隐式派生自 System.ValueType:
- 结构体:struct(直接派生于System.ValueType);
- 数值类型:
- 整 型: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);
- 浮点型:float(System.Single),double(System.Double);
- 用于财务计算的高精度decimal型:decimal(System.Decimal)。
- bool 型:bool(System.Boolean 的别名);
- 用户定义的结构体(派生于 System.ValueType)。
- 枚举:enum(派生于 System.Enum);
- 可空类型(派生于 System.Nullable<T> 泛型结构体,T 实际上是 System.Nullable<T> 的别名)。
C# 有以下一些引用类型:
- 数组(派生于System.Array)
- 用户用定义的以下类型:
- 类:class(派生于 System.Object);
- 接口:interface(接口不是一个“东西”,所以不存在派生于何处的问题。Anders 在《C# Programming Language》中说,接口只是表示一种约定 [contract]);
- 委托:delegate(派生于System.Delegate)。
- object(System.Object 的别名);
- 字符串:string(System.String 的别名)。
可以看出:
- 引用类型与值类型相同的是,结构体也可以实现接口;
- 引用类型可以派生出新的类型,而值类型不能;
- 引用类型可以包含null值,值类型不能(可空类型功能允许将 null 赋给值类型);
- 引用类型变量的赋值只复制对对象的引用,而不复制对象本身。而将一个值类型变量赋给另一个值类型变量时,将复制包含的值
现在举2个例子,对值类型和引用类型的变量赋值一一说明。
值类型变量的赋值: 值类型变量中保存的是实际数据,在赋值的时候只是把数据复制一份,然后赋给另一个变量。
例子1:
1 2 |
int var1=2; int var2=var1; //编译器会先复制var1的值,然后把它赋给var2.很明显var2的值也为2 |
引用类型变量的赋值: 引用类型变量中保存的是“指向实际数据的引用指针”。在进行赋值操作的时候,它和值类型一样,也是先有一个复制的操作,不过它复制的不是实际的数据,而是引用(真实数据的内存地址)。
所以引用类型的变量在赋值的时候,赋给另一变量的实际上是内存地址。这样赋值完成后,2 个引用变量中保存的是同一引用,他们的指向完全一样。
例子2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
class MyClass { public int val; } struct MyStruct { public int val; } class Program { static void Main(string[] args) { MyClass objectA=new MyClass(); MyClass objectB=objectA; //引用变量的赋值 赋值操作完成后,两个变量都指向同一内存地址 objectA.val=10; //给objectA.val赋值=10 由于objectB和objectA指向同一内存地址,所以ojbectB.val的值也为10 objectB.val=20; //给objectB.val赋值=20 由于objectB和objectA指向同一内存地址,所以objectA.val的值也为20 MyStruct structA=new MyStruct(); MyStruct structB=structA; //结构是值类型 赋值操作完成后,两个结构中的结构信息一致。注意是“结构中的信息”一致。 structA.val=30; structB.val=40; Console.WriteLine("objectA.val={0}", objectA.val); //输出结果是20 Console.WriteLine("objectB.val={0}", objectB.val); //输出结果是20 Console.WriteLine("structA.val={0}", structA.val); //输出结果是30 Console.WriteLine("structB.val={0}", structB.val); //输出结果是40 Console.ReadLine(); } } |
从上面 2 个例子可以看出,值类型变量的赋值操作,仅仅是 2 个实际数据之间的复制。而引用类型变量的赋值操作,复制的是引用,即内存地址,由于赋值后二者都指向同一内存地址,所以改变其中一个,另一个也会跟着改变,二者就像绑定在了一起。
除特别注明外,本站所有文章均为交通人原创,转载请注明出处来自http://www.hijtr.com/csharp-difference-between-value-type-and-reference-type/
暂无评论