1 什么是类型
1.1 又名数据类型
(1)A data type is a homogeneous collection of values,effectively presented, equipped with a setof operations which manipulate these values.
性质相同的值的集合有效的表达,配备了一系列专门针对这种类型的值的操作。 也可以说一种数据操作是专门针对某种类型的。
(2)是数据在内存中存储时的“型号”。 (内存:内部存储单元。当今的数据架构为冯诺依曼系统,运算器、控制器——这两个对应CPU,存储器——内存,输入输出系统;老师说如果想深入了解可以看下计算机科学中——《计算机组成原理》,关于硬件方面——单片机)
(就好比车的型号,小型车、中型车等,站3D的空间,数据类型是数据的型号,所占内存的空间。)
硬盘——扩展存储器。程序运行是在内存里运行的,而数据是在硬盘中保存的。关机了,数据存在硬盘里也不会丢,硬盘是用电磁来存储数据的。
软件 —— 操作系统 —— 硬件
(3)小内存容纳大尺寸数据会丢失精确度、发生错误 (发生错误就很麻烦了。)
(4)大内存容纳小尺寸数据会导致浪费 (①内存很少很贵,浪费内存就在浪费配置高内存的费用;②开发的软件内存太大,就会挤占其他程序内存,甚至系统内存,一开你开发的软件就跑的慢,那用户就不喜欢用你的软件,那销量就会大打折扣。)
(5)编程语言的数据类型与数学中的数据类型不完全相同。(同样一种数据类型的名字,在编程和数学中大多数情况下是一样的,有的时候会有比较显著的区别。比如:/ 这个运算符。数学3/4=0.75 ;编程:3/4=0。
1.2 强类型语言与弱类型语言的比较
介绍:当你用一门编程语言写程序的时候,如果数据受到数据类型的约束多,那就是强类型语言,反之是弱类型语言。
1.2.1 C语言示例:if条件
int x; //int类型是4个字节,每个字节8个bit,一共32位
x = 100;
long y; //long类型是8个字节,一共64位
y=100L;
x=100L; //这里就会报错
运用if语句,条件里只能使用布尔值,不能使用200=x这样的赋值。
C语言就可以这样编译,if的条件里只要不等于0,都是正确的,可以运行大括号里的值
1.2.2 JavaScript示例:动态类型——弱类型语言
给myVar先赋值了100,又赋值了Mr.Okay! 。 然后程序都是可以运行的,这就完全是弱类型语言。
运行:
1.2.3 C#语言对弱类型/动态类型的模仿
运用dynamic这个类型,就获得了类似JavaScript中var的功能。但我们只是模仿,对于初学者,知道有这回事就可以了,暂时还用不着。
2.类型在C#语言中的作用
2.1 一个C#类型中所包含的信息有
(1)存储此类型变量所需的内存空间大
(2)此类型的值可表示的最大、最小值范围
前缀带s的是有-号的,u的是不带-号的
浮点数:
(3)此类型所包含的成员(如方法、属性、事件等)
两大用处:静态用处、动态用处。这里指程序的静态与动态
一个程序在没有执行的时候,叫做静态时期
一个程序执行起来了,它就进入了动态时期,也叫作运行时期
遍历所有属性:
运行:
(4)此类型由何基类派生而来
C#有一个技能:反射
示例:
结果:
找寻它的基类:
结果:
(5)程序运行的时候,此类型的变量在分配在内存的什么位置
a.Stack简介 栈
栈的作用,是给方法(函数)调用来用的。
栈比较小,一般只有1-2G
b.Stack overflow——栈溢出
示例:
第二个示例:
stackalloc 是指直接从栈里切一块内存出去,这里用了一个肯定会爆栈的数组。因为这个是不安全的程序,所以要用unsafe,而且还要在VS程序用允许unsaft使用才能运行。C#在这方面保护机制做了很多,就是不想让你直接接触内存。
c.Heap简介 堆
堆是用来存储对象的,也就是实例(就是气球 ),是放在堆里面
堆比较大,一般达到几个G
d.使用Performance Monitor查看进程的堆内存使用量
Process 进程
一个程序从硬盘加载到内存,开始运行之后,就形成一个进程,就是这个程序正在运行的一个实例。
示例:
第一个按钮是增加内存使用,第二个是取消使用,但垃圾收集器还需要过段时间才能响应,通过Performance Monitor,可以查看进程的堆内存使用量,来判断你写的程序吃内存的情况。
Windows+R键 打开软件。
进程在Process里
在这里面找我们软件的名字
然后就可以跑内存数据了:
e.关于内存泄漏——内存的浪费(例如使用完对象忘记回收了)
比较C#和C++
在C++中,如果忘记回收对象了,那就是真正的泄漏了
在C#中,有一个垃圾收集器的机制,不用手动收集,垃圾收集器发现这个对象长期没人使用,就会回收。C#相对会安全一些
(6) 此类型所允许的操作(运算)
例如
3.0/4.0 = 0.75
3/4 = 0
3.C#语言的类型系统
3.1 C#的五大数据类型
3.1.1 类(Classes)
如Window,Form,Console,String
static void Main(string[] args) { Type mytype = typeof(Type); Console.WriteLine(mytype.FullName); //打印mytype所属的类 Console.WriteLine(mytype.IsClass); //打印mytype是不是一个类的结果 }
鼠标移到type,按F12——转到定义。老师说要多看这些
3.1.2 结构体(Structures)
如Int32,Int64,Single,Double
int类型:int类型太常用了,C#就把它吸收为关键字了。
3.1.3 枚举(Enumerations)
如HorizontalAlignment,Visibility
给一个集合,你只能从这个集合中选择一个值
例如form窗体,只能有三种状态:最大化、最小化、正常体
窗体显现:最大化
enum声明是用来定义枚举类型。
声明数据类型的关键字:
class 类
struct 结构体
enum 枚举
3.1.4 接口(Interfaces)
(高级,新手后续接触,先不讲)
3.1.5 委托(Delegates)
(高级,新手后续接触,老师先不讲)
3.2 C#类型的派生谱系
树状的,带有层级结构的。所有类型都以Object为基类型
(1)图中第一列关键字——引用类型相关
object 类
string 类
class 定义自己类的关键字
interface 定义自己类的关键字
delegate 定义自己类的关键字
(2)图中第二列关键字——值类型相关
横线以上蓝色的:真正的数据类型,对应的值类型
横线以下:
struct 用来定义自己的结构体的
enum 用来定义自己的枚举的
(3)图中第三列关键字
true,false :是bool类型的两个值
void:在函数不需要返回值的时候使用
null:引用变量里的值是空的,不引用任何实例
var,dynamic:用来声明变量的
(4)蓝色的表示:
①现成的数据类型,这些数据类型都是常用的,C#吸收为自己的关键字
②基本的数据类型,别的类型都是拿这些构成的。也叫C#语言内件数据类型
4.变量、对象与内存——核心内容
4.1什么是变量
4.1.1 表面上来看,变量的用途是存储数据
static void Main(string[] args)
{
int x; //声明了一个标准整数的类型变量,名字叫做x
x = 100; //用100这个值赋值给x,把100装进x
}
4.1.2 变量的概念之一
实际上,变量表示了存储位置,并且每个变量都有一个类型,以决定什么样的值能够存入变量
解释:
变量表示了存储位置:变量名表示(对应着)变量的值在内存中的存储位置。
4.1.3 变量一共有7种
- 静态变量: 一般叫静态字段
- 实例变量(也叫非静态变量;成员变量,字段): 一般叫字段
- 数组元素 :一般叫数组里面的元素
- 值参数(函数的参数):一般就叫参数
- 引用参数(函数的参数):一般就叫参数
- 输出形参(函数的参数):一般就叫参数
- 局部变量:一般叫变量
4.1.4 狭义的变量指局部变量,因为其他种类的变量都有自己的约定名称
简单地讲,局部变量就是方法体(函数体)里声明的变量
示例:静态变量、字段
class Program
{
static void Main(string[] args)
{
Student.Amount = 1;
Student stu= new Student();
stu.Name = "Test";
}
}
class Student
{
public static int Amount; //静态变量
public int Age; //实例变量:字段
public string Name;
}
示例:数组
int[] array = new int[100]; //100个4字节小隔间
array[99]=100;
示例:方法
class Student
{
public double Add(double a ,double b) //括号里的是值参数变量
{
return a+b;
}
}
引用参数变量:(内容较深,后续函数再讲)
class Student
{
public double Add(ref double a ,double b) //在值参数变量前添加ref,就变成引用参数变量
{
return a+b;
}
}
输出参数变量:(内容较深,后续函数再讲)
class Student
{
public double Add(out double a ,double b) //在值参数变量前添加out,就变成输出参数变量
{
return a+b;
}
}
示例:局部变量——在函数体里声明的变量
public static Add(double a,double b)
{
double result = a+b; //result就是局部变量
return result;
}
4.1.5 变量的声明
int a; //这就是声明了一个变量
a = 100;
int b;
b =200;
int c =a+b;
Console.WriteLine(c);
重点:有效的修饰符组合(opt——这个指可有可无的) 类型 变量名 初始化器(opt)
变量名必须是个名词
class Student
{
public static int Amount; //这里public static 就是有效的修饰符
public private static int Amount; //如果加上private,那么就会报错,这两个只能留一个
}
public static | int | Amount | = 100 |
有效修饰符组合 | 类型 | 变量名 | 初始化器 |
总结
变量=以变量名所对应的内存地址为起点,以其数据类型所要求的存储空间为长度的一块内存区域
4.2 值类型的变量
结构体类型是一种值类型,所以用结构体来演示值类型变量在内存中是如何存储的
4.2.1 以byte/sbyte/short/ushort为例(这些是结构体)
计算机是以字节存储和读取数据的。计算机为每一个字节都准备唯一编号
内存地址:字节在计算机科学中的编号。
橘黄色为系统程序,其他程序不能用。旁边空白的为自由内存。
(1)byte示例
byte b;
b=100;
按F1,跳转Viewer
大小:8位整数就是1个字节,byte就在空余的内存地方给8个bit。例如下图这里
如果byte b=100; 那么存储就是01100100
(2)sbyte示例
sbyte sb;
sb =100;
用第一位来表示正负,0表示+,1表示-。
100表示:
-100为按位取反在加1
①每一位都取反,0就变为1,1变为0
②这里的1就是最后1位加1,结果为:
(3)ushort
ushort us;
us =1000;
ushort表示没有符号的短整型
存储空间是2个字节16位:
范围:二进制16个1,换成十进制就是65535
us=1000存储情况:7比6高,所以要从高位开始存储(高高低低原则,高的8位对应高的8位,低的8位对应低的8位)
(4)short
short s;
s=-1000;
范围:开始一位做符号位,剩下15位。二进制15个1,换成十进制就是32767
1000:
-1000:按位取反再加一
按位取反:
再加一:
4.2.2 值类型没有实例,所谓的“实例”与变量合而为一
值类型给它赋了一个值,其实就可以理解是给了一个实例,所以说实例和变量合而为一
就像我们从来不这样写:
int x = new int[];
x = 100;
因为只要一写int x; 不写后面new int[]是一回事。
4.3 引用类型的变量与实例
4.1.3 引用类型变量与实例的关系:引用类型变量里存储的数据是对象的内存地址
他们的关系就像小朋友牵着气球一样。
class Program
{
static void Main(string[] args)
{
Student stu;
}
}
class Student
{
uint ID;
ushort Score;
}
uint 32位-4个字节
ushort 16位-2个字节
重点:这两个是成员变量,也叫字段!
引用变量:stu
值类型是按实际大小分配内存,而引用类型不是这样。
系统会直接给引用变量分配四个字节。把里面都刷成0
这里是 小朋友
这就表示整个变量没有引用任何实例。
static void Main(string[] args)
{
Student stu;
stu = new Student(); //这里才创建了实例
//包含了Student类中的ID和Score
}
创建了实例后,有了堆内存的地址,把它保存在stu中
class Student:
这里是 气球
前四个给uint,后两个给ushort
然后把内存地址30000001放入之前的四个字节中(也就是stu引用变量)。注意不是上图六个字节。
这里是 小朋友牵气球
也就是说:
引用变量存的是,实例在堆内存放的内存地址。
到这里,一个小朋友真正牵着一个气球了。
这也解释了,可以用两个引用变量来引用同一个实例。
static void Main(string[] args)
{
Student stu;
stu = new Student();
Student stu2;
stu2 = stu;
}
第一个绿块:stu; 第二个绿块:stu2
4.4 局部变量是stack(栈)上分配内存
比如刚才的Student stu。它是在Main这个函数中的,所以是局部变量,在栈上分配内存。
而Student类里两个成员变量,也叫字段。这两个会随着实例,分配到堆内存中。
4.5 变量默认值
一旦变量在内存中分配好了后,就默认给它都刷成0,这就是它的默认值。
但如果使用本地变量,没有赋值,那就不能编译。(C/C++可以)
C#要求本地变量必须要有明确的赋值。
4.6 常量(值不可改变的变量)
常量是不能再被赋值的。而且const这句赋值要一次性写完。
4.6装箱与拆箱(Boxing & Unboxing)※
实际使用越来越少(牵扯到性能的损失),但面试会考(数据类型在内存中是怎样起作用的)。
4.6.1 装箱
示例:
class Program
{
static void Main(string[] args)
{
int x = 100;
object obj = x; //所有类型都是所属object基类型,这里叫做装箱
}
}
x变量在内存中如下图:2进制的100
obj的内存如下图:
装箱的过程:obj要装x
①先把x内存拷贝到堆内存位置;
②再把x堆内存地址放到obj的内存里
这一套操作下来,肯定要损失系统性能。
4.6.2 拆箱
用y把x从obj中拆箱
static void Main(string[] args)
{
int x = 100;
object obj = x;
int y =(int)obj;
Console.WriteLine(y);
}
y变量:
把obj引用的值复制过来,y空间里存储的也是整数100的2进制值
从堆上往栈里搬东西,也会损失性能的。
4.6.2 拆箱
用y把x从obj中拆箱
static void Main(string[] args)
{
int x = 100;
object obj = x;
int y =(int)obj;
Console.WriteLine(y);
}
y变量:
把obj引用的值复制过来,y空间里存储的也是整数100的2进制值
从堆上往栈里搬东西,也会损失性能的。