数据类型
1. 值类型数据
值类型存储在栈内存中,变量名和变量值都具有固定的大小。声明变量时,会向栈内存申请存储空间,如果栈内存剩余的空间能容下申请的空间,那么就开拓申请的空间,让变量使用,否则抛出栈溢出异常。
C#中所有值类型数据直接映射到基础类库的基本类型。可以使用数值类型的IsValueType属性,判断该数据类型是否时值类型。
using System;
namespace DataTypeTest
{
public class ValueType
{
static void Main(string[] args)
{
int i = 10;
string s = "Hello World";
Console.WriteLine("{0}变量是否是值类型:{1}", i, i.GetType().IsValueType); // True
Console.WriteLine("{0}变量是否是值类型:{1}", s, s.GetType().IsValueType); // False
}
}
}
2. 引用类型数据
在C#中,引用类型存储在堆内存中,声明变量的类型是引用类型,那么公共运行环境受限在堆区中分配内存空间,再在栈区分配一个指向堆区内存地址的区域。
所谓“堆”,就是可以在堆内存的允许下,一个变量可以无限地扩大数据量。
C#中,引用类型有:接口、类、委托、集合、数组和字符串等。其中,字符串比较特殊,它的内容不可以修改,如果修改字符串的值,那么会在堆内存中重新分配内存空间,修改原字符串变量在栈内存的地址,使之指向该区域。
using System;
namespace DataTypeTest
{
public class PreferenceType
{
public PreferenceType()
{
}
static void Main(String[] args)
{
int[] a = new int[3] { 1, 2, 3 };
int[] b = a;
b[0] = 4;
Console.WriteLine("a[0]的值为:{0}", a[0]); // 4
}
}
}
3. 两者的区别
比较项目 | 值类型 | 引用类型 |
---|---|---|
内存位置 | 分配在栈区 | 分配在堆区 |
性能 | 读写效率高(不需地址转换) | 读写效率低(需要地址转换) |
释放 | 使用完毕后立即释放占用内存 | 使用完毕后由程序或垃圾回收器释放内存(不同的语言选择释放的方式不同,就那.NET平台的语言距离,C++是由用户自行释放,C#由垃圾回收器释放) |
赋值 | 直接在栈区进行内存复制 | 在栈区复制原对象的引用地址 |
拓展性 | 修改源代码会引起意外的改变 | 具有多态性,易拓展 |
4. 类型转换
C#类型转换分为显式和隐式
一种数据类型自动转换成另一种数据类型,称之为隐式转换,这种方式数据不会丢失,所以比较安全。
显式转换式强制性的,需要在变量之前指定类型变量的前缀,这种方式会造成数据丢失。显式转换时还需要注意数据类型的取值范围。但转换过程中数据类型过大不能实现显式转换时,程序将抛出范围过大的异常。
对于double类型,需要注意,所以的类型都可以隐式转换成double类型,但double类型只可以显式转换成float类型。
5. 装箱和拆箱
在C#中,值类型转换成引用类型称为装箱,引用类型转换成值类型称为拆箱。
using System;
namespace BoxingAndUnBoxingDemo
{
class Program
{
static void Main(string[] args)
{
short s = 1;
object o = s; //将short类型的s装箱至object类型的o中
int i = (int)(short)o; // 装箱后转换成int,不可写成(int)o,装箱和拆箱的前后数据类型必须对应
Console.WriteLine("i的值为:{0}", i); // 1
}
/*
注意:装箱后的数据不能够使用short以外的数据类型拆箱,必须使用装箱时的short数据类型才可以拆箱
*/
}
}
6. 范型
在C#中,范型是一种比较特殊的数据类型,公共语言运行时在内存为范型数据预留空间,等待满足范型约束条件的数据来补充。
但内存预留位置的数据填充完毕,数据类型得到确定,确定的过程就称为范型实例化。
在Java和C++中也存在范型的机制,但是他们和C#的实现方式不同,在Java中,范型编程基于基于数据类型的转换机制实现,即将使用范型的数据类型统一转换为Object类型。C++的范型编程基于模块的方式实现,即在编译时就对范型进行了实例化。而C#的范型编程是通过占位符的方式进行的。
using System;
namespace GenericDemo
{
class Program
{
/*
注意:值类型的数据类型是范型类Generics的约束,约束的方法是对范型类型名称使用where约束
C#还可以对范型类型的构造方法进行约束
class AGenericsClass<TypeOfValue, TypeOfPeferencet> where TypeOfValue : struct
where TypeOfReferencet : class,new()
在TypeOfReferencet范型上添加”new()“的约束,规定TypeOfReferencet类型必须能够使用无参数的构造方法创建范型类实例。
包括两个方面
* 具有无参数的构造函数,包含未声明的默认的构造函数
* 使用该无参数构造函数能够创建实例
*/
public class Generics<GenercsValue> where GenercsValue : struct
{
/// <summary>
/// 显示当前范型数据类型的名称
/// </summary>
/// <param name="a"></param>
/// <returns></returns>
public string Show(GenercsValue a)
{
return a.GetType().FullName;
}
}
static void Main(string[] args)
{
Generics<int> G1 = new Generics<int>();
Console.WriteLine("整形显示结果:{0}", G1.Show(3)); // System.Int32
Generics<float> G2 = new Generics<float>();
Console.WriteLine("浮点数显示结果:{0}", G2.Show(2.15f)); // System.Single
}
}
}