C# 类型基础(上)
C#类型都派生自System.Object
祖先的优良传统:Object的公共方法
Equals: 对象的同一性而非相等性
GetHashCode:返回对象的值的哈希码
ToString:默认返回类型的完整名称 this.GetType().FullName
GetType:返回从Type派生的一个对象的实例,即对象的元数据信息,此方法为非虚方法,为防止类来重写该方法,隐瞒真实的类型信息,从而破坏类型的安全性
GetHashCode有什么用?
判断对象是否相等的快速检查。Equals方法和GetHashCode方法的重写应该同时存在。如果Equals方法返回的结果是true,那么GetHashCode方法返回的结果应该相同。如果GetHashCode方法返回的结果相同,那么Equals方法返回的结果不一定是true。
public class Student
{
public string FirstName { get; private set; }
public string LastName { get; private set; } public Student(string firstName, string lastName)
{
this.FirstName = firstName;
this.LastName = lastName;
} public override bool Equals(object obj)
{
Console.WriteLine("Called Equals");
if (obj == null || this.GetType() != obj.GetType())
return false;
return this.FirstName == ((Student)obj).FirstName
&& this.LastName == ((Student)obj).LastName;
} public override int GetHashCode()
{
Console.WriteLine("Called GetHashCode");
return this.FirstName.GetHashCode();
}
} static void Main(string[] args)
{ Student student1 = new Student("Jun", "Lei");
Student student2 = new Student("Kaifu", "Li");
Student student3 = new Student("Jun", "Zhu"); //第一次调用
var dic = new Dictionary<Student, object>();
dic[student1] = new object();
Console.WriteLine("==================");
Console.WriteLine(dic.ContainsKey(student2));
Console.WriteLine("==================");
Console.WriteLine(dic.ContainsKey(student3));
//第二次调用
Student student4 = new Student("Jun", "Lei");
var dic1 = new Dictionary<Student, object>();
dic1[student4] = new object();
Console.WriteLine("==================");
Console.WriteLine(dic1.ContainsKey(new Student("Jun", "Lei"))); }
说明:在上面的Student类中我们重写了Equals和GetHashCode这两个基类的方法,在针对Dictionary这种散列值的key值判断的时候系统默认先调用GetHashCode判断,然后再掉用Equals方法判断,微软约定如果两个对象的哈希码不一样,则两个对象肯定不相等。所以在判断对象相等时系统默认先调用此方法做最简单的判断。举个例子:要比较两个人是不是兄弟,第一眼肯定是看两个人的性别是否相同(GetHashCode),如果性别都不一样,肯定不是兄弟,然后再判断长相,血型,DNA等的相似性(Equals)。
上述代码第一次调用时,输出结果如下:
在Student中我们重写了GetHashCode方法,让它获取FirstName的哈希值,这也就是意味着如果两个Student的FirstName相同的话这两个对象判断相等的时候系统默认返回的哈希值将是一样的,我们可以看到student1和student2 对象中的每一个字段都不相同,所以根绝GethashCode直接判断不相等,所以系统直接返回false,没有调用Equals方法判断。而Student3对象和Student1对象的FirstName是一样的,所以感觉哈希值已经判断不出来了,所以又调用了一次Equals方法进行深入判断,由于LastName不一样,所以返回结果为false。由上述代码我们可以得出结论,系统判断对象相等性的时候先调用GetHashCode判断,如果对象的哈希值一样,再根据Equals的实现判断,否则直接返回false。
如果我们把GetHashCode的实现注释掉,直接用系统的方法的话,返回结果如下:
因为Student1和Student3是两个不同的对象,所以哈希码肯定不一样,所以就不会调用Equals方法再继续进行判断了
基元类型
基元类型:编译器直接支持的数据类型称之为基元类型,它直接映射到Framework类库(FCL)中存在的类型,比如 int —> System.Int32
int a = 0; //最方便的语法
System.Int32 a = 0 ; //方便的语法
int a = new int() ; //不方便的语法
System.Int32 a = new System.Int32(); //最不方便的语法
问题1:以上四行代码生成的IL代码完全一样吗 ?(完全一样,可以根据生成的IL代码判断)
问题2:在32位操作系统上运行时,int代表32位整数,在64位操作系统上运行时,int代表64位整数这种说法正确吗?(完全错误,不管在什么操作系统上都是代表32位整数)
下面是C#中的基元类型和FCL中类型的对应关系
引用类型:
a.内存分配到托管堆上
b.托管堆上分配的每个对象都会包含:对象本身的值,类型对象指针,同步索引块
c.对象传递的是内存的引用地址
d.没有被使用的情况下,会被垃圾回收
eg:类都是引用类型,string
值类型:
a.内存分配到它声明的地方
b.分配的内存只包含本身的值
c.传递的是值本身
d.不会被垃圾回收
e.所有值类型都是隐式封闭的 sealed(防止一个值类型用作其他类型的基类型)
f.所有值类型都是 System.ValueType 的后代
eg: int , decimal , double, struct
关于引用类型的类型对象指针和同步索引块,下面的博客将的很详细,不过我还是没有完全理解,所有在此不做详细解释。
类型对象指针和同步索引块:http://www.cnblogs.com/yuyijq/archive/2009/03/13/1410071.html
Code1:
string str = "ab";
string str1 = str;
str = "abc";
str1 = ?
上面代码你一眼看过去肯定以为str1会输出 abc ,因为是引用类型啊,我们改了引用的值之后它也会跟这边,因为引用的是同一片内存地址呀。如果真这样认为那我只能说太年轻了。
输出依然是 ab,具体原因如下:
String的不可变性:string 对象是只读的,一旦创建了该对象,就不能修改该对象的值。看来似乎修改了,实际是string经过了特殊处理,每次改变值时都会建立一个新的string对象,变量会指向这个新的对象,而原来的还是指向原来的对象,所以不会改变。这也是string效率低下的原因,如果经常改变string的值则应该使用StringBuilder而不使用string
StringBulider: 通过分配一个缓存(工作区)来解决这些问题,在工作区中对字符串应用StringBuilder类的相关方法。包括添加,删除,移除,插入和替换字符等等。执行完之后,将调用ToString方法把工作区中的内容转换为一个字符串,方便赋给一个字符串变量。这样StringBuilder会提升一些性能。
Code2:
string str1 = "string" ;
string str2 = "string" ;
Console .WriteLine(string .ReferenceEquals(str1, str2));
我们虽然初始化了两个字符串对象,看起来是分配了两个内存地址,但其实不是的,CLR对此作了优化,具体解释如下:
当CLR初始化的时,会创建一个内部的散列表,Key为字符串,Value为指向托管堆中字符串对象的引用。当构造str1时,先会去散列表中查询是否存在”string”字符串,如果不存在那么会在托管堆中构造一个新的String对象,然后将”string”字符串和指向该对象的引用添加到散列表中,当构造str2时,由于散列表中存在 Key为”string”的引用,于是将Value值赋值给str2,那么str1和str2引用的是同一个String对象
注意:string 是不可变的引用的类型,CLR在运行时进行了优化,相同的string实际上在内存中只存在一份
Code3:
public struct Point
{
public int x = 0;
public int y = 0;
}
上面的代码是编译不通过的,原因是:
值类型不支持无参数构造函数,当一个值类型没有提供任何有参构造函数的时候,是不能够对字段在定义中进行初始化。
为什么微软不支持值类型的无参构造函数?
防止开发人员不去显式调用构造函数,对于值类型的变量来说,如果不去显式调用构造函数的话,编译器不会在生成的IL代码中添加调用构造函数的代码的。
C# 类型基础(上)的更多相关文章
- 《C#从现象到本质》读书笔记(二)第2章 C#类型基础(上)
<C#从现象到本质>读书笔记第二篇 第2章 C#类型基础(上) 类型指的是集合{类,结构,接口,枚举,委托}中的任意一个成员.任何拥有某类型的值(value)称为某类型的一个实例(inst ...
- C#学习笔记——面向对象、面向组件以及类型基础
C#学习笔记——面向对象.面向组件以及类型基础 目录 一 面向对象与面向组件 二 基元类型与 new 操作 三 值类型与引用类型 四 类型转换 五 相等性与同一性 六 对象哈希码 一 面向对象与面向组 ...
- 在Livemedia的基础上开发自己的流媒体客户端
一.背景 二.Livemedia框架介绍 1.总体框架 2.客户端框架 2.1 客户端openRTSP流程 2.2增加一种新的媒体 2.2.1增加媒体的format 2.2.2 新媒体需要考虑的问题 ...
- 使用mysqlbinlog工具的基础上及时恢复数据的位置或点
使用mysqlbinlog工具的基础上及时恢复的位置或点 MySQL备份一般采取完全备份的形式加日志备份.让我们运行一个完整备份,每天.每小时运行二进制日志备份. 这样在MySQL Server故障后 ...
- [CLR via C#]4. 类型基础及类型、对象、栈和堆运行时的相互联系
原文:[CLR via C#]4. 类型基础及类型.对象.栈和堆运行时的相互联系 CLR要求所有类型最终都要从System.Object派生.也就是所,下面的两个定义是完全相同的, //隐式派生自Sy ...
- UNIX基础上
时光飞逝,转眼已经毕业快2年了,觉得自己学的东西多却不精.对此深深的思考一下,觉得有必要连载unix环境编程文章,以此激励自己学习.在此立贴为证,2天一篇博客从零开始阐述unix的环境编程. 参考书籍 ...
- 如何基于Winform开发框架或混合框架基础上进行项目的快速开发
在开发项目的时候,我们为了提高速度和质量,往往不是白手起家,需要基于一定的基础上进行项目的快速开发,这样可以利用整个框架的生态基础模块,以及成熟统一的开发方式,可以极大提高我们开发的效率.本篇随笔就是 ...
- 《C#从现象到本质》读书笔记(三)第3章C#类型基础(下)
<C#从现象到本质>读书笔记第3章C#类型基础(下) 常量以关键字const修饰.C#支持静态字段(类型字段)和实例字段. 无参属性的get方法不支持参数,而有参属性的get方法支持传入一 ...
- [No0000B9]C# 类型基础 值类型和引用类型 及其 对象复制 浅度复制vs深度复制 深入研究2
接上[No0000B5]C# 类型基础 值类型和引用类型 及其 对象判等 深入研究1 对象复制 有的时候,创建一个对象可能会非常耗时,比如对象需要从远程数据库中获取数据来填充,又或者创建对象需要读取硬 ...
- [No0000B5]C# 类型基础 值类型和引用类型 及其 对象判等 深入研究1
引言 本文之初的目的是讲述设计模式中的 Prototype(原型)模式,但是如果想较清楚地弄明白这个模式,需要了解对象克隆(Object Clone),Clone其实也就是对象复制.复制又分为了浅度复 ...
随机推荐
- Python日志监控系统处理日志(pyinotify)
前言 最近项目中遇到一个用于监控日志文件的Python包pyinotify,结合自己的项目经验和网上的一些资料总结一下,总的原理是利用pyinotify模块监控日志文件夹,当日志到来的情况下,触发相应 ...
- spring mvc的跨域解决方案
什么是跨域 一句话:同一个ip.同一个网络协议.同一个端口,三者都满足就是同一个域,否则就是跨域. 为什么非得跨域 基于两个方面: a. web应用本身是部署在不同的服务器上 b.基于开发的角度 -- ...
- RHM-M60型挖掘机力矩限制器/载荷指示器
RHM-M60挖掘机力矩限制器RHM-M60 excavator crane moment limiter RHM-M60型挖掘机力矩限制器是臂架型起重机机械的安全保护装置,本产品采用32位高 ...
- MyBatis之基于XML的属性与列名映射
上一博客主要是对单表的增删改查,比较简单,而且每个属性与table表的列都是一一对应名字也一样,今天主要学习属性与table表列名不一致的处理,主要有两种一是属性与列名不一致,二是枚举的情况,这里暂时 ...
- malloc函数用法
malloc函数用法 函数声明(函数原型): void *malloc(int size); 说明:malloc 向系统申请分配指定size个字节的内存空间.返回类型是 void* 类型.void* ...
- MIT公开课:算法导论 笔记(一)
课程链接:http://open.163.com/special/opencourse/algorithms.html 第一课:算法分析基础 1.介绍插入排序与归并排序,计算并比较最坏运行时间 2.算 ...
- docfx(二)
1. 初始化一个docfx项目 1.创建一个文件夹D:\docfx_walkthrough 2.运行cmd 到该文件下执行命令D:\docfx_walkthrough 3.输入命令 docfx ini ...
- sublimeserver启动本地服务器(sublime text)
今天又get到了一个新知识点,就是在sublime text上也可以模拟一个本地服务器的环境,前提是要先安装sublimeserver这个插件.这个插件的安装办法有两种. 1.我们可以直接在subli ...
- 安卓Acitivity的启动模式
活动的四大启动模式 Ps:除了standar模式外,其他启动模式都要在AndroidManifest.xml中设置 android:lauchMode的值 安卓活动的启动模式(LaunchMode)有 ...
- api接口token验证
接口特点汇总: 1.因为是非开放性的,所以所有的接口都是封闭的,只对公司内部的产品有效: 2.因为是非开放性的,所以OAuth那套协议是行不通的,因为没有中间用户的授权过程: 3.有点接口需要用户登录 ...