-
C# 数据类型详解以及变量、对象与内存
1、什么是类型?
类型又称为数据类型(Data Type),数据类型在数据结构中的定义是一个值的集合以及定义在这个值集上的一组操作。
可以简单理解为数据在内存中存储的“型号”;小内存容纳大尺寸数据会丢失精准度,发生错误;而大内存容纳小尺寸数据会导致浪费。
还应注意编成语言的数据类型与数学中的数据类型不完全相同,例如数学中 3/4=0.75 而在C# 语言中 3 /4 =0。
不同的编程语言对数据类型的约束程度不一样,所以有强类型编程语言和弱类型编程语言的区分。
变量是用来存储值得所在处,它们有名字和数据类型。变量的数据类型决定了如何将代表这些值的位存储到计算机的内存中。
我们都知道,计算机的世界是二进制的,仅仅用0和1就构建了所有的表达,计算机用来存储0或者1的单位就是位(bit),8个位组成一个字节(byte)。
2、数据类型在C#语言中的作用
一个C#类型中所包含的信息有:
- 存储此类型变量所需的内存空间大小 如 int 类型需要4个字节也就是4*8=32位进行存储
- 此类型的值可表示的最大、最小值范围 sbyte占一个字节8位,s前缀表示带符号位,取值范围-128到127,byte占一个字节,不带符号位,取值范围位0-255。
- 此类型所包含的成员(如方法、属性、事件等) 可以用于ide的错误识别和反射时动态调用
- 此类型由何基类派生而来
- 程序运行的时候,此类型的变量在分配在内存的什么位置(栈或堆)
- 此类型所允许的操作(运算)
3、程序的内存使用分析
- Stack简介:函数调用使用,函数调用实际可以理解位栈帧的入栈出栈操作。空间较小,一般1-2M
- Stack overflow 空间较小所以会出现栈空间溢出的情况。例如递归函数未正常结束。
- Heap简介:用来存储对象(实例)使用,空间较大,可以达到数G
- 使用Perfomance Monitor观察内存使用情况
- 关于内存泄漏 未使用的对象未赋值为null导致垃圾收集器未处理,导致空间浪费,这称为内存泄漏。
这里给出观察内存使用情况的一个样例,新增的wpf程序,使用Winform程序也是可行的。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using System.Windows; 7 using System.Windows.Controls; 8 using System.Windows.Data; 9 using System.Windows.Documents; 10 using System.Windows.Input; 11 using System.Windows.Media; 12 using System.Windows.Media.Imaging; 13 using System.Windows.Navigation; 14 using System.Windows.Shapes; 15 16 namespace HeapSample 17 { 18 /// <summary> 19 /// MainWindow.xaml 的交互逻辑 20 /// </summary> 21 public partial class MainWindow : Window 22 { 23 public MainWindow() 24 { 25 InitializeComponent(); 26 } 27 28 List<Window> winList; 29 30 private void Btn_Consume_Click(object sender, RoutedEventArgs e) 31 { 32 winList = new List<Window>(); 33 for (int i = 0; i < 20000; i++) 34 { 35 Window wd = new Window(); 36 winList.Add(wd); 37 } 38 } 39 40 private void Btn_ClearHeap_Click(object sender, RoutedEventArgs e) 41 { 42 winList.Clear(); 43 } 44 } 45 }
至于Perfomance Monitor观察内存使用情况,可以查看 https:////www.cnblogs.com/FreeLoopPowter/p/12298482.html 。
4、C# 数据类型系统
C#的五大数据类型
- 类(class):如 Form,Window,Console.String
- 结构体(structres):如 Int32,Int64,SIngle,Double
- 枚举(Enumerations):如Visibility,WindoState
- 接口(Interfaces)
- 委托(Delegate)
C#类型的派生谱系
这里顺带提一点,我们经常在编辑器中使用结构体时,例如定义一个32位的整型变量时,我们基本使用 int a=5; 这样的形式,这并不是前面说的结构体类型包含的内容,为啥没有报错呢?
这是由于某些数据类型如此常用,以至于许多编辑器允许代码以简化语法来操纵它们(也叫关键字)。这种语法不仅增强了代码的可读性和书写的方便性,生成的IL代码还与使用对应的FCL类库中的类型一致。
这类编译器能直接支持的类型称为 基元类型。基元类型直接映射到Framework类库(FCL)中存在的类型,可以通过按 F12 查看定义,就可以看到映射的FCL类型。
查看CLR Via C#一书,发现其对值类型和引用类型的阐述非常明了,还包括了对值类型和引用类型内存分配的讲解。这里选取重要内容进行记录,方便自己后续温习。对数据类型的内存分配情况的分析有利于后续理解方法的传值参数、引用参数、输出参数的理解。
CLR支持两种类型:引用类型和值类型。虽然FCL的大多数类型都是引用类型,但程序员使用最多的还是值类型。引用类型总是从托管堆中分配,C#的new 操作符返回对象内存地址——即指向对象数据的内存地址。使用引用类型必须留意性能问题,首先认清楚以下四个事实。
- 内存必须从托管堆分配。
- 堆上分配的每个对象都有一些额外成员。
- 对象中的其他字段(为字段而设)总是设为零。
- 从托管堆分配对象时,可能强制执行一次垃圾回收。(当垃圾回收器发现内存不够时会强制执行一次垃圾回收)
如果所有类型都是引用类型,应用程序的性能将显著下降。设想每次使用 Int32 时都进行一次内存分配,性能会收到多么大的影响!为了提升简单和常用的数据类型的性能,CLR提供了名为“值类型”的轻量级类型。值类型的实例一般是在线程栈上分配(虽然也可作为字段嵌入引用类型的对象中),在代表值类型实例的变量中不包含指向实例的指针。变量中包含了实例本身的字段。由于变量已包含了实例的字段,所以操作实例中的字段不需要提取指针。值类型的实例不受垃圾回收器的控制。因此,值类型的使用缓解了托管堆的压力,并减少了应用程序生存期间的垃圾回收次数。
下面给出一个示例,结合图示讲解值类型和引用类型的区别。引用类型变量对对象实例的引用实际上是以在栈中分配的引用类型变量存放堆中实例的起始内存地址来实现的。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace RefTypeAndValueTypeApp 8 { 9 class Program 10 { 11 static void Main(string[] args) 12 { 13 //在堆上分配空间 14 SomeRef r1 = new SomeRef(); 15 //在栈上分配空间 16 SomeValue v1 = new SomeValue(); 17 r1.x = 5;//提取指针,给指针引用的对象赋值 18 v1.x = 5;//在栈上修改,由0修改为5 19 Console.WriteLine(r1.x);//显示 "5" 20 Console.WriteLine(v1.x);//同样显示 "5" 21 //以上内存分配情况见图左边 22 23 //只复制引用(指针) 24 SomeRef r2 = r1; 25 //在栈上分配并复制成员 26 SomeValue v2 = v1; 27 r1.x = 8;//r1.x 和 r2.x 都会改变 28 v1.x = 9;//v2.x会改变 v1.x不会改变 29 Console.WriteLine(r1.x);//显示 "8" 30 Console.WriteLine(r2.x);//同样显示 "8" 31 Console.WriteLine(v1.x);//显示 "9" 32 Console.WriteLine(v2.x);//显示 "5" 33 //以上部分程序执行的内存分配情况见图右侧 34 35 //让控制台等待输入,有输入后才终止 36 Console.ReadKey(); 37 } 38 } 39 /// <summary> 40 /// 引用类型 41 /// </summary> 42 class SomeRef 43 { 44 public Int32 x; 45 } 46 47 /// <summary> 48 /// 值类型 49 /// </summary> 50 struct SomeValue 51 { 52 public Int32 x; 53 } 54 }
书写这篇随笔的主要目的是自己温习和防遗忘,本着分享的心态公开,如有不妥之处,还请指出,相互交流。