【C# 基础】类型相关

  • C# 所有类型都从 System.Object 派生,不论隐式和显式
    • System.Object 的公共方法
      • Equals:两个对象有相同的值,返回 true
      • GetHashCode:如果类型对象要在哈希表集合中作为 key 使用,重写该方法
      • ToString:默认返回类型完整名称
      • GetType:返回从 Type 派生的一个类型的实例
  • new 操作符所做的事情
    • 1. 计算类型及其所有积累性中定义的所有实例字段需要的字节数
      • 堆上的每个对象都需要 overhead 成员,用于给 CLR 管理对象,包括 "类型对象指针" 和 "同步块索引"
    • 2. 从托管对中分配类型要求的字节数,从而分配对象的内存,分配的所有字节都设为0
    • 3. 初始化对象的 "类型对象指针" 和 "同步块索引"
    • 4. 调用类型的实例构造器,传递在 new 调用中指定的实参。
    • !!没有和 new 对应的 delete 操作符
  • 类型转换
    • CLR 最重要的特性之一就是类型安全
    • C# 不要求任何特殊语法即可将对象转换为它的任何基类型,这是安全的隐式转换
    • C# 要求将对象转换为它的某个派生类型时,只能显式转换
    • 使用 is 和 as 操作符来转型
      Object o new Object();
      Boolean b1 = (o is Object);  // b1 为 true
      Boolean b2 = (o is A);       // b2 为 false
      
      // 通常使用
      if (o is A) {
          A a = (A)o;
      }
      
      // as 用法
      A a = o as A;
      if (a != null) {
          // 使用 a
      }
  • 类型,对象,线程栈和托管堆
    • 栈空间用于向方法传递实参,方法内部定义的局部变量也在栈上。栈从高位内存地址向地位构建
    • 对象中包含类型对象指针和同步块索引。
      • 任何时候在堆上新建对象,CLR 都自动初始化内部的 "类型对象指针" 成员来引用和对象对应的类型对象
      • 此外,再调用类型的构造器之前,CLR 会先初始化同步块索引,并将对象所有实例字段设为 null 或 0
      • 静态方法调用:CLR 会定位与定义静态方法的类型对应的类型对象,然后 JIT 编译器在类型对象的方法表中查找与被调用方法对应的记录项,对方法进行 JIT 编译
      • 非虚实例方法调用:JIT 编译器会找到与 "发出调用的那个变量的类型" 对应的类型对象,如果没有找到,则回溯一直到 Object ,然后 JIT 编译器在类型对象方法表中查找引用了被调用方法的记录项,进行 JIT 编译,再调用
      • 虚实例方法调用:比较特殊,会生成额外代码,方法每次调用都会调用额外代码。
        • 这些代码首先检查发出调用的变量,并跟随地址来到发出调用的对象
        • 然后检查对象内部的 "类型对象指针",该成员指向对象的实际类型
        • 在类型对象方法表中查找引用了被调用方法的记录项,进行 JIT 编译,再调用
      • CLR 内部做了些什么
        • 对象包含的 "类型对象指针" 成员本质也是对象,CLR 创建类型对象时,必须初始化这些成员
        • CLR 开始在一个进程中运行时,会为 MSCoreLib.dll 中定义的 System.Type 类型创建一个特殊的类型对象,作为所有要创建对象的实例
        • 所以都会被初始化成对 System.Type 的引用
        • System.Type 也是一个对象,它内部也有 "类型对象指针" ,指向它本身
        • System.Type 的 GetType 方法返回存储在指定对象的 "类型对象指针" 成员中的地址
  • 基元类型
    • int a = 0; 与 System.Int32 a = new System.Int32(); 生成的 IL 代码相同
    • C# 的 string 直接映射到 System.String,int 始终是 System.Int32,long 映射到 System.Int64
    • C# 允许隐式转换必须安全,所以低精度转到高精度可以进行,但是高精度转到低精度必须显式进行
  • 引用类型和值类型
    • 引用类型必须留意性能问题
      • 引用类型总是从托管堆分配,C# 的 new 操作符返回对象内存地址
      • 堆上分配的每个对象都有额外成员,这些成员必须初始化
      • 对象中的其他字节总是为0
      • 从托管堆分配对象时,可能强制执行一次垃圾回收
      • System.Exception 类,System.IO.FileStream类,System.Random类,System.String类
    • 由于每一次分配都会进行内存分配,性能很差,所以提供值类型
      • 一般在线程栈上分配,也会出现在引用类型对象内,从而在堆上分配
      • 值类型实例的变量中不包含指向实例的指针
      • 值类型实例不受垃圾回收器的控制,缓解托管堆的压力,并减少垃圾回收次数
      • 结构或枚举,System.Int32,System.Boolean,System.Decimal,System.TimeSpan结构,System.DayOfWeek,System.IO.FileAttributes,System.Drawing.FontStyle 枚举
      • 所有结构都是抽象类型 System.ValueType 的直接派生类,所有枚举都是 System.Enum 派生,它又是从System.ValueType 派生出来的
    • 除非满足以下全部条件,否则不应将类型声明为值类型
      • 类型具有基元类型的行为。没有成员会修改类型的任何实例字段
      • 类型不需要从其他任何类型继承
      • 类型也不派生出其他任何类型
      • 同时满足一下任何条件
        • 类型的实例较小(16字节或更小)
        • 类型的实例较大(大于16字节),但不作为方法实参传递,也不从方法返回
    • 值类型和引用类型区别
      • 值类型有两种形式:未装箱和已装箱,引用类型是已装箱的
      • 值类型从 System.ValueType 派生,它重写了 System.Object 的 Equals 和 GetHashCode 方法,定义自己值类型时,要显式重写
      • 值类型不能作为基类,所以不应有虚方法,抽象方法,所有方法都隐式密封
      • 引用类型默认初始化 null ,值类型总是包含基础类型的一个值,值类型所有成员都初始化为 0,当然也可声明可空类型
      • 值类型赋值会逐字段赋值,引用类型只复制内存地址。两个或多个引用类型变量能引用堆中同一个对象,对一个变量执行的操作可能影响到另一个变量引用的对象,值类型变量不会
      • 未装箱的值类型不在堆上分配,一旦定义了该类型的一个实例的方法不再活动,为他们分配的存储就会被释放
    • 值类型的装箱和拆箱
      • 原因:值类型不作为对象在托管堆中分配,不被垃圾回收,也不通过指针引用,但很多时候需要获取对值类型实例的引用,例如 ArrayList 中存储值类型,而 ArrayList 添加必须是 Object 参数,所以必须将值类型转换成对象,并获取对象的引用,这个转换操作就是装箱机制
      • 装箱操作
        • 在托管堆中分配内存。分配的内存量是值类型个字段所需的内存量加上托管堆所有对象都有的额外成员的内存量
        • 值类型的字段复制到新分配的堆内存
        • 返回对象地址。这个地址是对象引用了,转换成引用类型了
      • 拆箱操作
        • 获取引用,将对象中所有字段复制到值类型变量中
        • 复制分两步
          • 获取已装箱对象中各个字段的地址,叫做拆箱
          • 将字段包含的值从堆复制到基于栈的值类型实例中
        • 如果已装箱变量为 null 抛出 NullReferenceException 异常
        • 如果引用的对象不是所需值类型的已装箱实例,抛出 InvalidCastException 异常
        • 拆箱代价比装箱低很多
    • 对象相等性和同一性
      • 比较对象,比如对象放到集合中去,写代码对集合中的对象排序,搜索和比较
      • System.Object 提供了名为 Equal 的虚方法,作用是在两个对象包含相同值得前提下返回 true,它的实现实际是同一性,而非相等性,所以默认实现并不合理
      • Equals 实现:
        • obj 实参为 null,就返回 false
        • 如果 this 和 obj 实参引用同一个对象,就返回 true
        • 如果this 和 obj 实参引用不同类型的对象,就返回 false
        • 针对类型定义的每个实例字段,将 this 对象中的值与 obj 对象中的值进行比较。任何不相等,就返回 false
        • 重写 Equals 方法可能还需要做下面几件事
          • 让类型实现 System.IEquatable<T> 接口的 Equals 方法
          • 重载 == 和 != 操作符方法
        • 如果有出于排序目的而比较类型的实例,类型还应实现 System.IComparable 的 CompareTo
      • 检查同一性务必调用 ReferenceEquals
    • 对象哈希码
      • 类型重写 Equals 后,还应该重写 GetHashCode 方法,否则编译器会生成警告
      • 这个算法要提供良好的随机分布,使哈希表获得最佳性能
      • 可在算法中调用基类的 GetHashCode 方法,并包含它的返回值。一般不要调用 Object 或 ValueType 的 GetHashCode 方法,性能较差
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值