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

    基元类型与FCL类型

    编译器直接支持的数据类型称为基元类型。基元类型直接映射到Framework类库(FCL)中存在的类型。例如C#的int直接映射到System.Int32类型。

    从另一个角度,可以认为C#编译器自动假定所有源代码文件都添加了以下using指令

    using byte=System.Byte
    using int=System.Int32

    基元类型相互转换

    虽然int和float是不同的类型,相互不存在派生关系,但是事实上编译和运行都没问题。

    因为C#编译器非常熟悉基元类型,会在编译代码时应用自己的特殊规则。编译器能执行基元类型之间的隐式或显式转型。

    只有在转换安全的时候,C#才允许隐式转换。所谓安全是指不会发生数据丢失,比如int转换到float。

    但如果可能不安全,C#就要求显式转型。不安全意味着转换后可能丢失精度或数量级。例如int转换为byte。

     


    基元类型溢出检查

    对基元类型执行的许多算术运算都可能造成溢出。

    Byte b=100;
    b=(Byte)(b+200);//b现在包含44(或者十六进制2C)

    可以通过checked和unchecked操作符来提供灵活的溢出检查。

    //使用unchecked操作符
    int invalid=unchecked((int)(-1));//OK
    //使用checked操作符
    byteb=100;
    b=checked((byte)(b+200));//抛出OverflowException异常

    除了两种操作符,C#还支持checked和unchecked语句,他们使一个块中的所有表达式都进行或不进行溢出检查。

    checked{
        byte b=100;
        b+=200;//该表达式会进行溢出检查
    }

    CLR不将Decimal视为基元类型,如果对Decimal类型使用checked和unchecked操作符,执行的运算是不安全的。

     


    动态类型Dynamic

    使用动态类型的需求

    C#所有表达式都解析成类型的实例,编译器生成的代码只执行对该类型有效的操作。

    即错误在编译时检测到,执行前是正确的。但程序许多时候仍需处理一些运行时才会知晓的信息。

    为了方便开发人员使用反射或者与其他组件通信,C#编译器允许将表达式的类型标记为dynamic。还可将表达式的结果放到变量中,并将变量类型标记为dynamic。

    运行时决定具体的类型

    代码使用dynamic表达式调用成员时,编译器生成特殊的IL代码来描述所需的操作。这种特殊的代码称为有效载荷payload。

    在运行时,payload代码根据dynamic表达式、变量引用的对象的实际类型来决定具体执行的操作。

    internal static class DynamicDemo{
        public static void Main(){
            dynamic value;
            for(int demo=0;demo<2;demo++){
                value=(demo==0)?(dynamic)5:(dynamic)"A";
                value=value+value;
                M(value);
            }
        }
        private static void M(Int32 n){Console.WriteLine("M(Int32):"+n);}
        private static void M(String s){Console.WriteLine("M(String):"+s);}
    }

    执行Main会得到以下输出:

    M(int):10
    M(string):AA

    由于value是dynamic,所以C#编译器生成payload代码在运行时检查value的实际类型,决定+操作符实际要干什么。

    dynamic的本质是object

    如果字段、方法参数或方法返回值的类型是dynamic,编译器会将该类型转换为System.Object,

    并在元数据中向字段、参数、或返回类型应用System.Runtime.CompilerServices.DynamicAttribute的实例。

    dynamic类型的局部变量

    如果局部变量被指定为dynamic,则变量类型也会成为Object。但不会向局部变量应用DynamicAttribute,因为他限制在方法内部使用。

    由于dynamic其实就是Object,所以方法签名不能仅靠dynamic和Object的变化来区分。

    泛型代码中不生成有效载荷

    泛型类(引用类型)、结构(值类型)、接口、委托或方法的泛型类型实参也可以是dynamic类型。

    编译器将dynamic转换成Object,并向必要的各种元数据类型应用DynamicAttribute。

    使用的泛型代码是已经编译好的,会将类型视为Object。编译器不在泛型代码中生成payload代码,所以不会执行动态调度。

    dynamic类型的隐式转型

    所有表达式都能隐式转型为dynamic,因为所有表达式最终都生成从Object派生的类型。

    正常情况,编译器不允许写代码将表达式从Object隐式转型为其他类型,必须显式转型。

    但是,编译器允许使用隐式转型语法将表达式从dynamic转型为其他类型。

    Object o1=123;//OK,从Int32隐式转型为Object,装箱
    Int32 n1=o1;//Error,不允许从Object到Int32的隐式转型
    Int32 n2=(Int32)o1;//OK,从Object显式转型为Int32,拆箱
    
    dynamic d1=123;//Ok,从Int32隐式转型为dynamic,装箱
    In32 n3=d1;//OK,从dynamic隐式转型为Int32,拆箱

    从dynamic转型为其他类型时,虽然编译器允许省略显式转型,但CLR会在运行时验证转型来确保类型安全性。

    如果对象类型不兼容要转换成的类型,CLR会抛出异常。注意,dynamic表达式的求值结果是一个动态表达式。

    dynamic和var

    dynamic d=123;
    var result=M(d);//注意,var result等同于dynamic result

    代码之所以能通过编译,是因为编译时不知道调用哪个M方法,从而不知道M的返回类型。

    所以编译器假定result变量具有dynamic类型。 如果M方法的返回类型时void,会抛出异常。

    不要混淆dynamic和var

    var声明局部变量只是一种简化语法,var只能在方法内部声明局部变量,而dynamic可用于局部变量、字段和参数。

    表达式不能转型为var,但能转型为dynamic。而且必须显式初始化用var声明的变量,但无需初始化用dynamic声明的变量。

    dynamic的限制

    dynamic只能访问对象的实例成员,因为dynamic变量必须引用对象。

  • 相关阅读:
    PTA9
    PTA8
    第七周
    第六周
    第五周
    PTA4
    2019第三次作业
    第十周课程总结
    第九周课程总结&实验报告(七)
    第八周课程总结&实验报告(六)
  • 原文地址:https://www.cnblogs.com/errornull/p/9743295.html
Copyright © 2011-2022 走看看