zoukankan      html  css  js  c++  java
  • 第二部分 设计类型:第5章 基元类型、引用类型和值类型

    5.1编程语言的基元类型

    基元类型:编译器直接支持的数据类型。
    基元类型直接映射到Framework类库中存在的类型。如C#中,int->System.Int32
    简化过程:
    System.Int32 a = new System.Int32();
    int a = new int();
    System.Int32 a = 0;
    int a = 0;

    编译器自动在所有源代码文件中添加using指令:
    using int = System.Int32;
    ...

    C#语言规范推荐使用关键字,而不是完整类型名称。
    作者认为应该使用完整类型名称,原因如下:
    1.写法的困惑。string与String
    2.语言的差异。C#中long映射到System.Int64,但其他语言可能不一样。
    3.FCL(Framework Class Library)许多方法都将类型名称作为方法名的一部分。
    BinaryReader br = new BinaryReader(...);
    float val = br.ReadSingle();//正确,但感觉不自然。
    Single val = br.ReadSingle();//正确,而且让人一目了然。
    4.c#程序员的习惯问题。

    显示和隐式转换:
    不丢失精度,转化为范围更大的为隐式转换。
    丢失精度,转化为范围更小的为显示转换。

    C#编译器采用截断处理:6.8最终将放6到Int32中。其他编译器可能是向上取整。

    基本类型能写成文本常量(literal),文本常量可被看成是类型本身的一个实例。
    Console.WriteLine(123.ToString() + 456.ToString()); //"123456"

    如果一个表达式由文本常量构成,编译器能在编译时完成表达式的求值,增强程序性能:
    Boolean found = false; //生成的代码将found设为0
    Int32 x = 100 + 20 + 3; //生成的代码将x设为123
    String s = "a " + "bc"; //生成的代码将s设为"a bc"
    编译器知道顺序解析操作符(+,-,++,--等)

    checked和unchecked基元类型操作
    UInt32 invalid = unchecked(UInt32) (-1); //OK 不检查是否溢出

    Byte b = 100;
    b = checked((Byte)(b + 200)); //抛出OverflowException异常

    语句:
    checked{
     Byte b = 100;
     b = (Byte)(b+200); //该表达式会进行溢出检查
     SomeMethod(400); //调用方法不会对方法造成任何影响
    }
    checked、unchecked操作符和语句唯一作用为:决定生成哪一个版本的加减乘和数据转换IL指令。

    建议:
    1.尽量使用有符号数值类型(如,Int32和Int64),不要使用无符号数值类型(UInt32、Uint64),无符号数值类型不相容于CLS。类库的多个部分被硬编码为返回有符号的值(如Array和String的Length属性)。
    2.代码可能发生你不希望的溢出,把这些代码放到checked块中,同时捕捉OverflowException,从容得体地从错误中恢复。
    3.将允许发生溢出的代码放到unchecked块中,比如计算一个校验和的时候。
    4.对于没有使用checked或unchecked的任何代码,都假定你希望在发生溢出时抛出一个异常,比如在输入是已知的前提下计算一些东西(比如质素),此时的溢出应被记为bug。

    开发时,打开编译器/checked+开关来调试性生成。生成 - 高级 - 检查运算上溢/下溢
    正式发布时,改为/checked-

    5.2引用类型和值类型


    CLR支持两种类型:引用类型 + 值类型
    引用类型总是从托管堆上分配的。

    值类型的实例一般分配在线程栈上(虽然也可作为字段嵌入引用类型的对象中)。
    值类型的变量包含了实例本身的字段。
    值类型=结构+枚举
    System.Object=>System.ValueType=>结构
    System.Object=>System.ValueType=>System.Enum抽象类=>枚举
    所有值类型必须从System.ValueType派生
    一个值类型可以实现一个或多个接口。
    值类型都是隐式密封的(sealed),防止作为其他类型的基类型。

    一般值类型的性能更好,定义类型的时候可以先考虑定义成值类型,不过要求比较严格,大部分情况还是定义为引用类型。

    5.3 值类型的装箱和拆箱


    装箱:值类型=>引用类型

    装箱操作内部发生的事:
    1.在托管堆中分配好内存。内存=值类型字段需要内存+托管堆所有对象都有的两个额外成员(类型对象指针、同步块索引)需要的内存
    2.值类型的字段复制到新分配的堆内存。
    3.返回对象的地址(对象的引用)。值类型现在是一个引用类型。

    拆箱:引用类型=>值类型 获取指针的过程,指针指向对象中的原始值类型(数据字段),指向的是已装箱实例中的未装箱部分。

    已装箱的值类型实例在拆箱时内部发生的事情:
    1.如果包含了“对已装箱值类型实例的引用”的变量为null,就抛出NullReferenceException异常。
    2.如果引用指向的对象不是所期待的值类型的一个已装箱实例,就抛出一个InvalidCastException异常。

    未装箱值类型比引用类型更“轻型”的原因:
    1.它们不在托管堆上分配。
    2.它们没有堆上的每个对象的额外成员(类型对象指针、同步块索引)。

    5.4 对象哈希码

    System.Object提供了虚方法GetHashCode获取任意对象的Int32哈希码。
    如果重写了Equals那么还应该重写下GetHashCode,因为两个对象是否相等是看哈希码是否一样。

    •集合中添加一个键值对时,首先获取键对象的一个哈希码,这个哈希码指出键值对应该存储到哪个哈希桶中。
    •集合查找键时,会获取指定的键对象的哈希码,这个哈希码标识了现在要搜索的目标哈希桶,在其中查找与指定键对象相等的一个键对象。
    •一旦修改了集合中的一个键对象,集合就再也找不到对象了。所以,要修改一个哈希表中的键值对象时,正确的做法:移除原来的键值对,修改键对象,再将新的键值对添加回哈希表中。

    5.5 dynamic基元类型

    C#是一种类型安全的语言,所有表达式都解析成某个类型的实例,编译器生成的代码中,只会执行对这个类型来说有效的操作。

    •为了方便开发人员使用反射或与基本组件通信,C#编译器允许将一个表达式的类型标记为dynamic,还可将表达式结果放到变量中,并将变量的类型标记为dynamic。
    •用这个dynamic表达式/变量调用一个成员,如字段、属性/索引器、方法、委托、一元/二元/转换操作符。
    •代码使用dynamic表达式/变量调用一个成员时,编译器会生成特殊的IL代码(payload有效载荷)来描述所需的操作,在运行时,payload代码根据当前由dynamic表达式/变量引用的对象的实际类型来决定具体执行的操作。

  • 相关阅读:
    ObjectDataSource用法之六(刪除)
    ObjectDataSourc用法之七(新增)
    C# 装箱和拆箱
    Android SD卡中压缩包解压(ZIP文件)
    Android 调用系统的拨号服务实现 电话拨打功能
    Android 判断SD卡存不存在
    android中IdleHandler的使用
    android使用遥控器模拟鼠标拖拽操作
    Android SD卡 文件或目录拷贝、复制、粘贴
    C#在线获取歌词(转)
  • 原文地址:https://www.cnblogs.com/zxx193/p/3559847.html
Copyright © 2011-2022 走看看