zoukankan      html  css  js  c++  java
  • CLR via C#-类型基础

    一、所有类型都从System.Object派生

    运行时要求每个类型最终都从System.Object类型派生,也就是说,以下两个类型定义完全一样

    //隐式派生自System.Object
    class Employee{}
    //显式派生自Object
    class Employee:System.Object{}

    System.Object类提供了如下所示的公共实例方法,所以每个类型的每个对象都保证了一组最基本的方法。

    Equals()//如果两个对象有相同的值,返回true​
    GetHashCode()//返回对象值的哈希码
    ToString()//返回类型的完整名称
    GetType()//返回从Type派生的一个类型的实例,指出调用该方法的对象是什么类型

    二、实例化对象时new操作符所做的事情

    计算类型及其所有基类型中定义的所有实例字段需要的字节数。堆上每个对象都需要一些额外的成员,包括类型对象指针和同步块索引。CLR利用这些成员管理对象,额外成员的字节数要计入对象大小。

    从托管堆中分配类型要求的字节数,从而分配对象的内存,分配的所有字节都设为0。

    初始化对象的类型对象指针和同步块索引成员。

    ④调用类型的实例构造器,传递在new调用中指定的实参。大多数编译器都在构造器中自动生成代码来调用基类构造器。

    Employee e=new Employee("ConstructorParam1");

    new执行了所有这些操作后,返回指向新建对象一个引用或指针。

    如上示例代码中,该引用保存到变量e中,后者具有Employee类型。

    实例对象内存的释放

    C#没有和new操作符对应的delete操作符,没有办法显式释放为对象分配的内存。

    不过CLR采用了垃圾自动回收机制,能自动检测到一个对象不再被使用或访问并自动释放对象的内存。


    三、类型转换

    类型安全性

    CLR最重要的特性之一就是类型安全,调用GetType方法获取对象的确切类型。

    由于GetType方法是虚方法,所以一个类型不可能伪装成另一个类型。

    例如Employee类型不能重写GetType方法返回一个SuperHero类型。

    向基类型转换:隐式转换

    C#不要求任何特殊语法即可将对象转换为他的任何基类型,向基类型的转换被认为是一种安全的隐式转换。

    向派生类转换:显式转换

    将对象转换为他的某个派生类型时,C#只能显式转换,因为这种转换可能在运行时失败

    //该类型隐式派生自System.Object
    internal class Employee{}
    //测试类
    Object o=new Employee();//不需要转型,new返回一个Employee对象,而Object是Employee的基类型
    Employee e=(Employee)o;//需要转型,Employee派生自Object

    在运行时,CLR检查转型操作,确定总是转换为对象的实际类型或者他的任何基类型。

    使用C#的is操作符进行转型

    is检查对象是否兼容于指定类型。返回Boolean值true或false,is操作符永不抛出异常。

    例如以下代码:

    Object o=new Object();
    Boolean b1=(o is Object);//b1为true
    Boolean b2=(o is Employee);//b2为false

    如果对象引用null,is操作符总是返回false,因为没有可检查其类型的对象。

    is操作符通常这样使用:

    if(o is Employee){
      Employee e=(Employee)o;
    }

    在上述代码中,CLR实际检查两次对象类型,is操作符首先核实o是否兼容与Employee类型。

    如果是,在if语句内转型,CLR再次核实o是否引用一个Employee。

    使用C#的as操作符进行转型

    CLR的类型检查增强了安全性,但无疑会对性能造成一定影响,这是因为CLR首先必须判断变量的引用的对象的实际类型。

    CLR必须遍历继承层结构,用每个基类型去核对指定的类型。

    由于这是一个常用的编程模式,C#专门提供了as操作符,简化这种代码的写法并提升性能。

    Employee e=o as Employee;
    if(e!=null){
        //在if语句中使用e
    }

    在上述代码中,CLR核实o是否兼容于Employee类型。如果是,as返回对同一个对象的非null引用,如果不兼容,返回null。

    as操作符使CLR只校验一次对象类型,if语句只检查e是否为null,速度比校验对象的类型快得多。

    as操作符的工作方式与强制类型转换一样,只是它永不抛出异常,如果对象不能转型,结果就是null,企图直接使用最终生成的引用会抛出System.NullReferenceException异常。

    以下代码对此进行了演示:

    Object o = new Object();//新建Object对象
    Employee e=o as Employee;//将o转型为Employee
    //上述转型会失败,不抛出异常,但e被设为null
    e.ToString();//访问e抛出NullReferenceException异常

    四、命名空间和程序集

    使用using指令简化命名空间

    C#编译器通过using指令提供这个机制,例如:

    using System.IO;
    using System.Text;

    只需要在代码中输入FileStream和StringBuilder这两个简化的类型名称,编译器就会自动将引用展开成System.IO.FileStream和System.Text.StringBuilder。

    使用using指令创建别名

    可能两个或更多类型在不同命名空间中同名。比如使用如下所示代码的引用,二者都有Wifget的类型。

    微软强烈建议为类型定义具有唯一性的名称。为了消除歧义,必须显式告诉编译器创建哪个的。

    using Microsoft;//尝试附加"Microsoft."前缀
    using Wintellect;//尝试附加"Wintellect."前缀

    而using指令的另一种形式允许为类型或命名空间创建别名,以消除不同命名空间的重名的类的歧义。

    //将WinerllectWidget 符号定义成Wintellect.Widget的别名
    using WinerllectWidget = Wintellect.Widget

    外部别名

    C#还提供了外部别名(extern alias)的功能,来解决命名空间和类型都重名的情况。

    外部别名还允许从同一程序集的两个不同版本中访问一个类型

    命名空间和程序集的关系

    命名空间和程序集(实现类型的文件)不一定相关,特别是,同一个命名空间中的类型不可能在不同程序集中实现。同一个程序集也可能包含不同命名空间的类型。

  • 相关阅读:
    堆栈学习
    需要阅读的书籍
    Rust Book Lang Ch.19 Fully Qualified Syntax, Supertraits, Newtype Pattern, type aliases, never type, dynamic sized type
    Rust Lang Book Ch.19 Placeholder type, Default generic type parameter, operator overloading
    Rust Lang Book Ch.19 Unsafe
    Rust Lang Book Ch.18 Patterns and Matching
    Rust Lang Book Ch.17 OOP
    Rust Lang Book Ch.16 Concurrency
    Rust Lang Book Ch.15 Smart Pointers
    HDU3966-Aragorn's Story-树链剖分-点权
  • 原文地址:https://www.cnblogs.com/errornull/p/9741276.html
Copyright © 2011-2022 走看看