C#语言——类型、变量与对象
什么是类型(Type)
类型又名数据类型,是数据在内存中存储时的“型号”,小内存容纳大尺寸数据会丢失精确度、发生错误,大内存容纳小尺寸数据会导致浪费,编程语言的数据类型与数据的数据类型不完全相同。
强类型语言与弱类型语言的比较
核心区别:强类型语言在编译或运行时严格检查变量类型,不允许未经显式转换的跨类型操作;弱类型语言类型检查宽松,支持隐式类型转换,甚至允许变量类型随值变化。
两者的关键差异可通过以下3点对比:
1. 类型检查时机与严格度
- 强类型:编译期(如Java、C#)或运行期(如Python、Go)强制校验类型,例如 int 型变量不能直接与 string 型变量运算,必须手动转换。
- 弱类型:仅在运行期松散校验,允许隐式转换,例如JavaScript中 1 + "2" 会自动转为字符串拼接,结果为 "12" 。
2. 灵活性与安全性
- 强类型:灵活性低,但能提前规避“类型不匹配”bug,适合大型复杂项目(如企业系统),降低维护成本。
- 弱类型:灵活性高,语法简洁、开发效率快,但隐藏的类型问题可能在运行时爆发,适合小型脚本或快速迭代场景(如前端交互)。
3. 典型代表
- 强类型语言:Java、C#、Python、Go、Rust。
- 弱类型语言:JavaScript、PHP、Perl。
类型在C#语言中的作用
一个C#类型中所包含的信息有:
存储此类型变量所需的内存空间大小
此类型的值可表示的最大、最小值范围
此类型所包含到底成员(如方法、属性、事件等)
此类型由何基类派生而来
此类型所允许的操作(运算)
程序运行时,此类型的变量分配在内存的什么位置
Stack
栈中装载方法调用
栈比较小,只有一两兆Heap
堆中存储对象
堆比较大,有几个G
C#语言的类型系统
C#的五大数据类型
类:如Window,Form,Console,String
结构体:如Int32,Int64,Single,Double
枚举:如HorizontalAlignment,Visibility
接口、委托
变量、对象与内存
什么是变量
表面上来看,变量的用途是存储数据
实际上,变量表示了存储位置,并且每个变量都有一个类型,以决定什么样的值能够存入变量
变量一共有七种:静态变量、实例变量(成员变量,字段)、数组元素、值参数、引用参数、输出形参、局部变量
侠义的变量指局部变量,因为其他种类的变量都有自己的约定名称
简单的讲,局部变量就是方法体(函数体)里的变量
变量的声明
有效的修饰符组合 类型 变量名 初始化器
变量=以变量名所对应的内存地址为起点、以其数据类型所要求的存储空间为长度的一块内存区域
值类型的变量
值类型没有实例,所谓的“实例”与变量合二为一
引用类型的变量与实例
引用类型变量与实例的关系:引用类型变量里存储的数据是对象的内存地址(在堆上的)
局部变量是在stack上分配内存
变量的默认值
namespace Text {class Program{static void Main(string[] args){Student stu = new Student();Console.WriteLine(stu.age);Console.WriteLine(stu.name);}}class Student{public int age;public string name;} }
本地变量必须初始化值,不会有默认值
常量(值不可改变的变量):用const修饰
装箱与拆箱
一个值类型转换为对象类型时,则被称为装箱;用于在垃圾回收堆中储存值类型。装箱是值类型到Object类型或到此类型所实现的任何接口类型的 隐式转换。
当一个对象类型转换为值类型时,则被称为拆箱。从object类型到值类型或从接口类型到实现该接口的值类型的 显示转换。
利用装箱和拆箱功能,通过允许值类型的任何值与Object类型的值进行相互转换,将引用类型与值类型连接起来。
⚠只有装过箱的对象才能拆箱;
装箱: 对值类型在堆中分配一个对象实例,并将该值复制到新的对象中。
按三步进行:
第一步:在堆上分配一个内存空间,大小等于需要装箱的值类型对象的大小加上两个引用类型对象都拥有的成员:类型对象指针和同步块引用。
第二步:将值类型的实例字段拷贝到新分配的内存中。
第三步:返回一个指向堆上新对象的引用,并且存储到堆栈上被装箱的那个值类型的对象里。
这个地址就是一个指向对象的引用了。
拆箱:所谓的拆箱,就是装箱操作的反操作,把堆中的对象复制到堆栈中,并且返回其值。需要注意的是,拆箱操作将判断被拆箱的对象类型和将要被复制的值类型引用是否一致,检查对象实例,确保它是给定值类型的一个装箱值。将该值从实例复制到值类型变量中。如果不一致,将会抛出一个InvalidCastException的异常。这里的类型匹配并不采用任何显示的类型转换。【以下代码说明了这一特性】
有书上讲,拆箱只是获取引用对象中指向值类型部分的指针,而内容拷贝则是赋值语句之触发。
static void Main(string[] args) {try{Int32 i = 3;// 装箱Object o = i;// 拆箱,类型转换失败Int16 j = (Int16)o;}catch (Exception ex){Console.WriteLine(ex.Message);}Int32 ii = 3;// 装箱Object obj = ii;// 拆箱Int16 jj = (Int16)(Int32)obj;Console.WriteLine("拆箱成功!");Console.ReadKey(); }