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变量必须引用对象。

  • 相关阅读:
    101. Symmetric Tree(js)
    100. Same Tree(js)
    99. Recover Binary Search Tree(js)
    98. Validate Binary Search Tree(js)
    97. Interleaving String(js)
    96. Unique Binary Search Trees(js)
    95. Unique Binary Search Trees II(js)
    94. Binary Tree Inorder Traversal(js)
    93. Restore IP Addresses(js)
    92. Reverse Linked List II(js)
  • 原文地址:https://www.cnblogs.com/errornull/p/9743295.html
Copyright © 2011-2022 走看看