基元类型和FCL类型

FCL类型就是指Int32这种类型,这是CLR支持的类型。

而基元类型就是指int这种类型,这是C#编译器支持的,实际上在编译后,还是会被转为Int32类型。

而且学过C的朋友肯定记得,int在32位机器和64位机器字节数可能不同,但是C#.NET里int就是表示Int32。

因为在基元类型和FCL类型之间,有一个一一对应的映射关系。另外注意dynamic实际上对应的类型就是Object,只是说C#编译器允许用简单的语法让dynamic变量参与动态调度。

表达式由字面量构成,编译器在编译的时候就能完成表达式求值

Boolean found=false;//生成的代码将found设为0
Int32 x=++;//x设为123
String a="a"+"bc";//s设为“abc”

checked和unchecked基元类型操作

此指令就用来检查溢出和不检查溢出,而默认是unchecked,不过这个可以改。检查溢出就报异常,不检查溢出就回滚。

引用类型和值类型

所谓值类型的去看它们类型的定义,比如Int32和DayOfWeek都是struct和enum类型,而struct类型实际上派生自System.ValueType类型,而Enum类型派生自System.Enum类型。而Enum类型最终还是派生自System.ValueType类型。

好吧,他们俩个的差异性其实还是蛮多的,不过基本上这是最基础的了,而且基本上是本书就讲,所以反而懒得写了。

装箱与拆箱

装箱就是把本来在栈中的值类型,在堆中新开辟一个内存空间,把值类型的数据复制进去,并增加引用类型都有的类型指针和同步块索引,然后返回这个内存空间引用地址。

拆箱就是反过来,先获取装箱对象中各个字段的地址,再将这些字段包含的值从堆复制到栈。

由上面看出装箱拆箱其实很影响效率,所以写代码的时候应该避免。

另外装箱的值类型,调用自身的函数修改自己的字段的时候并不会修改堆里的数据,只会先转换为值类型,再修改栈里的数据。

如果要修改堆里的数据,只能定义一个接口,让值类型里的函数去实现这个接口,然后想修改堆里的数据,那么就把对象转换为这个接口,那么去修改的话,就会修改堆里的数据。

如果你看不懂上面的话,那么请慎用值类型,作者推荐不要定义任何会修改实例字段的属性或者方法,甚至可以将值类型的所有字段都加上readonly。

其实结构体什么的用类就好了,大的结构体传参啊什么的,搞不好还会引起栈溢出。毕竟每个线程也就1MB的栈空间。

对象的相等性和同一性

之前我们讲过System.Object提供了名为Equals的虚方法,也就是说所有的对象都是有的,作用实在两个对象包含相同值的前提下返回true。

然而这个方法只是比较了同一性,而不是相等性。

实际上就是对应的同一性就是指两个对象的引用相同,也就是说它们指向同一个对象。

相等性如字面意思可知。也就说如果具备同一性,那么一定具备相等性。

由于Equals是个虚方法,可以重写,所以并不一定就是这个用法。(System.ValueType就重写了,Equals实现的是相等性而不是统一性。但是这个Equals是里的实现步骤用到了反射,而反射这个东西又是比较慢的,所以定义自己的值类型时可以考虑重写,从而提高性能。)

而==这个操作符也是可以重载的,除非你在比较之前,将两个对象的类型都转换为object。

于是Object又提供了一个静态方法,Object.ReferenceEquals,效果就如上所述,比较同一性。

注意,如果自己去定义一个值类型,然后重写Equals方法去实现相等性。那么应该注意让类型实现IEquatable<T>接口的Equals方法(通常实现的Equals方法除了调用自己的类型参数,还应该有一个重载函数调用object参数,以便在内部调用类型安全的Equals方法。这个定义接收object对象的重写函数么就是对IEquatable<T>的Equals的实现),还有重写==和!=操作符方法。

考虑到排序,所以可能还需要实现IComparable的CompareTo方法,和IComparable<T>的类型安全的CompareTo方法。实现了这些方法,那么<,<=,>,>=在内部调用类型安全的CompareTo方法也OK了。

(如果你觉得上面自己定义值类型的实现还有什么地方觉得遗漏,最好的方法其实就是去看int类型的定义就ok了)

对象哈希码

System.Object提供了虚方法GetHashCode,它能获取任意对象的Int32哈希码。

另外重写了Equals方法,那么最好重写GetHashCode方法。

因为在System.Collections.Hashtable类型和System.Collections.Generic.Dictionary类型以及其它的一些集合的实现中,要求两个对象必须要有相同的Hash码才被视为相等。

所以重写Equals方法实现相等性后,最好也重写GetHashCode方法,以确保相等型算法和对象哈希码算法一致。

另外重写时可以调用基类的GetHashCode方法,但是不要调用Object或者ValueType的GetHashCode方法,因为两者的实现性能不高。

包含相同值的两个不同对象应返回相同的哈希码。(作者建议不要对哈希码进行持久化,因为哈希码的算法可能会改变)

dynamic基元类型

dynamic基元类型是为了方便开发人员使用反射或者与其它非.net组件通信.

代码使用dynamic表达式/变量时,编译器生成特殊的IL代码来描述这种操作。这种特殊的代码被称为payload(有效载荷)。在运行时,payload根据dynamic表达式/变量引用的对象的实际类型来决定具体执行的操作。

dynamic类型在编译后实际上是作为System.object,然而它在元数据中被应用了System.Runtime.CompilerServices.DynamicAttribute的实例。局部变量除外,因为Attribute显然不能在方法内部使用。

另外使用的泛型的dynamic的代码时,泛型代码已经变异好了,将类型视为Object,编译器不在泛型代码中生成payload,所以也不会执行动态调度。

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

dynamic a=;
Int32 b=a;

另外dynamic表达式的求值结果也是一个dynamic类型。

不能定义对dynamic进行扩展的扩展方法,不能将lambda表达式或匿名方法作为实参传给dynamic使用。

为COM对象生成可由“运行时”调用的包装时。Com组件的方法中使用任何Variant实际都转化为dynamic,这称为动态化。显著简化了与COM对象的操作。

当然用dynamic会有额外的性能开销,因为会引用一些必须的dll,然后执行一些动态绑定啊什么的。如果只是一两处用这个东西,还是用传统方法好一点。(一般会引用Microsoft.CSharp.dll,与com组件操作还会用到System.Dynamic.dll)

【C#进阶系列】05 基元类型、引用类型和值类型的更多相关文章

  1. CLR via C#(02)-基元类型、引用类型、值类型

    http://www.cnblogs.com/qq0827/p/3281150.html 一. 基元类型 编译器能够直接支持的数据类型叫做基元类型.例如int, string等.基元类型和.NET框架 ...

  2. [CLR via C#]引用类型和值类型

    一.引用类型与值类型的区别 CLR支持两种类型:引用类型和值类型.引用类型总是从托管堆上分配的,C#的new操作符会返回对象的内存地址.使用引用类型时,必须注意到一些性能问题. 1)内存必须从托管堆上 ...

  3. 《CLR via C#》读书笔记--基元类型、引用类型和值类型

    编程语言的基元类型 编译器直接支持的数据类型称为基元类型.基元类型直接映射到Framework类库中存在的类型.例如:C#中的int直接映射到System.Int32类型.下表给出了C#基元类型与对应 ...

  4. 《CLR via C#》读书笔记(5)基元类型、引用类型和值类型

    5.1 基元类型 编译器直接支持的数据类型称为基元类型(primitive type). 以下4行到吗生成完全相同的IL int a = 0; //最方便的语法 System.Int32 b = 0; ...

  5. 【CLR Via C#】第5章 基元类型、引用类型、值类型

    第二遍看这本书,决定记录一下加深印象. 1,基元类型 什么事基元类型?基元类型是直接映射到FrameWork类库(FCL)中存在的类型,编译器直接支持的数据类型.比如int直接映射到System.In ...

  6. CLR via C#深解笔记三 - 基元类型、引用类型和值类型 | 类型和成员基础 | 常量和字段

    编程语言的基元类型   某些数据类型如此常用,以至于许多编译器允许代码以简化的语法来操纵它们. System.Int32 a = new System.Int32();  // a = 0 a = 1 ...

  7. CLR VIA C#: 基元类型、 引用类型 和 值类型

    一.基元类型 . 引用类型 和 值类型的区别: 1.基元类型(primitive type):编译器直接支持的数据类型: 基元类型 直接映射到 FCL 中存在的类型. C# 小写是基元类型,例如:st ...

  8. <NET CLR via c# 第4版>笔记 第5章 基元类型、引用类型和值类型

    5.1 编程语言的基元类型 c#不管在什么操作系统上运行,int始终映射到System.Int32; long始终映射到System.Int64 可以通过checked/unchecked操作符/语句 ...

  9. 重温CLR(四)基元类型、引用类型、值类型

    编程语言的基元类型 编译器直接支持的数据类型称为基元类型(primitive type).基元类型直接映射到framework类型(fcl)中存在的类型. 下表列出fcl类型 从另一个角度,可以认为C ...

随机推荐

  1. 301 redirect Domain Name using global.asax

    void Application_BeginRequest(object sender, EventArgs e) { if (HttpContext.Current.Request.Url.ToSt ...

  2. JCEF3——谷歌浏览器内核Java版实现(一):使用jawt获取窗体句柄

    前言 最近一段时间研究谷歌浏览器内核.谷歌浏览器内核一直开源,并维护更新,它的开源项目中内核更新速度和Chrome浏览器版本更新进度一样!而且它不同于WebKit(值得一题的是谷歌浏览器已不使用Web ...

  3. Linux网络流量实时监控ifstat iftop命令详解

    ifstat 介绍 ifstat工具是个网络接口监测工具,比较简单看网络流量 实例 默认使用 #ifstat        eth0                eth1       KB /s i ...

  4. db2安装及卸载

    创建用户和组: #创建组信息 groupadd -g db2iadm1 groupadd -g db2fadm1 groupadd -g dasadm1 #创建用户信息 useradd -u -g d ...

  5. git 在提交之前撤销add操作

    问题 在使用git时,在未添加.ignore文件前使用 git add . 将所有文件添加到库中,不小心将一些不需要加入版本库的文件加到了版本库中.由于此时还没有提交所以不存在HEAD版本,不能使用 ...

  6. mono+jexus 验证码不显示:System.Drawing

    System.ArgumentException The requested FontFamily could not be found [GDI+ status: FontFamilyNotFoun ...

  7. [转]Android dex分包方案

    转载自:https://m.oschina.net/blog/308583 当一个app的功能越来越复杂,代码量越来越多,也许有一天便会突然遇到下列现象: 1. 生成的apk在2.3以前的机器无法安装 ...

  8. Android中使用自身携带的Junit新建一个测试工程

    1.新建立一个Android工程 package com.shellway.junit; public class Service { public int divide(int a,int b){ ...

  9. Java知多少(110)数据库之插入记录

    插入数据表记录有3种方案 一.使用Statement对象 实现插入数据表记录的SQL语句的语法是: insert into 表名(字段名1,字段名2,……)value (字段值1,字段值2,……) 例 ...

  10. MVC过滤器中获取实体类属性值

    本文地址:http://www.cnblogs.com/outtamyhead/p/3616913.html,转载请保留本地址! 最近在项目遇到了这个问题:获取Action行参中实体类的属性值,主要的 ...