代码改变世界

随笔分类 -  CLR via C# 第三版

5.3.1 使用接口更改已装箱值类型中的字段(以及为什么不应该这样做)

2012-01-05 10:21 by iRead, 417 阅读, 收藏,
摘要: 下面让我们通过一些例子来验证自己对值类型、装箱和拆箱的理解程度。请研究以下代码,判断它会在控制台上显示什么: using System; //Point是一个值类型 internal struct Point{ private Int32 m_x,m_y; public Point(Int32 x,Int32 y){ m_x = x; m_y = y; } public void Change(Int32 x,Int32 y){ m_x = x; m_y = y; } public override Stri... 阅读全文

5.3 值类型的装箱和拆箱

2012-01-04 17:00 by iRead, 727 阅读, 收藏,
摘要: 值类型是比引用类型更“轻型”的一种类型,因为它们不作为对象在托管堆中分配,不会被垃圾回收,也不通过指针来引用。但在许多情况下,都需要获取对值类型的一个实例的引用。例如,假定要创建一个ArrayList对象(System.Collections命名空间中定义的一个类型)类容纳一组Point结构,那么代码可能像下面这样: //声明一个值类型 struct Point{ public Int32 x,y; } public sealed class Program{ public static void Main(){ ArrayList a = new Array... 阅读全文

5.2 引用类型和值类型

2012-01-03 11:25 by iRead, 287 阅读, 收藏,
摘要: CLR支持两种类型:引用类型和值类型。虽然FCL中的大多数类型都是引用类型,但程序员用的最多的还是值类型。引用类型总是从托管堆上分配的,C#的new操作符会返回对象的内存地址—也就是指向对象数据的内存地址。使用引用类型时,必须注意到一些性能问题。首先考虑以下事实:内存必须从托管堆上分配对上分配的每个对象都有一些额外的成员,这些成员必须初始化对象中的其他字节(为字段而设)总是设为零从托管堆上分配一个对象时,可能强制执行一次垃圾收集操作 如果所有类型都是引用类型,引用程序的性能将显著下降。设想假如每次使用一个Int32值时,都进行一次内存分配,性能会受到多么大的影响!为了提升简单的、常用的类... 阅读全文

5.1.1 checked和unchecked基元类型操作

2011-12-30 10:52 by iRead, 460 阅读, 收藏,
摘要: 对基元类型执行的许多算术运算都可能造成溢出: Byte b = 100; b = (Byte) (b+200) ; //b现在包含44(或者十六进制值2C)重要提示:执行上述算术运算时,第一步要求所有操作数都扩大为32位值(或者64位值,如果任何操作数需要超过32位来表示的话)。所以,b和200(这两个值都不超过32位)首先转换成32位值,然后加到一起。结果是一个32位值(十进制300,或十六进制12C)。该值在存回变量b之前,必须转型为一个Byte。C#不会隐式执行这个转型操作,这正是第二行代码需要强制转换为Byte的原因。 在大多数变成情形中,这种情悄悄发生的溢出是我们不希望的。... 阅读全文

5.1 编程语言的基元类型

2011-12-23 10:23 by iRead, 333 阅读, 收藏,
摘要: 某些数据类型如此常用,以至于许多编译器允许代码以简化的语法来操作它们。例如,可以使用以下语法来分配一个整数: System.Int32 a = new System.Int32(); 但你肯定不愿意使用这种语法来声明并初始化一个整数,它实在是太繁琐了。幸运的是,包括C#在内的许多编译器都允许换用如下所示的语法: int a = 0; 这种语法不仅增强了代码的可读性,而且生成的IL代码与使用System.Int32时生成的IL代码是完全一致的。编译器直接支持的数据类型成为基元类型(primitive type)。基元类型直接映射到Framework类库(FCL)中存在的类型。比如在C... 阅读全文

第5章 基元类型、引用类型和值类型

2011-12-23 10:19 by iRead, 254 阅读, 收藏,
摘要: 本章内容:编程语言的基元类型引用类型和值类型值类型的装箱和拆箱对象哈希码Dynamic基元类型 本章将讨论Microsoft.NET Framework开发人员经常会接触到的各种类型。所有开发人员都应该熟悉这些类型的不同行为。我首次接触.NET Framework时,并不完全理解基元类型、引用类型和值类型的区别,则造成我在代码中会不知不觉引入一些不易察觉的bug和性能问题。通过解释类型之前的区别,希望开发人员能避免我所经历过的麻烦,并提高代码效率。 阅读全文

4.4 运行时的相互联系

2011-12-22 16:36 by iRead, 309 阅读, 收藏,
摘要: 本节将解释类型、对象、线程栈和托管堆在运行时的相互关系。此外,还将解释调用静态方法、实例方法和虚方法的区别。首先从一些计算机基础知识开始。 虽然下面要讨论的东西不是CLR特有的,但掌握了这些只是之后,就有了一个良好的理论基础。接着,就可以修改我们的讨论,介绍CLR特有的内容。 图4-2展示了已记载了CLR的一个Microsoft Windows进程。在这个进程中,可能存在多个线程。一个线程创建时,会分配到一个1MB大小的栈。这个栈的空间用于向方法传递实参,并用于方法内部定义的局部变量。图4-2展示了一个线程的栈内存(右侧)。栈是从高位内存地址向低位内存地址构建的。在图中,线程已执行了一... 阅读全文

4.3 命名空间和程序集

2011-12-09 10:10 by iRead, 658 阅读, 收藏,
摘要: 命名空间(namespace)用于对相应的类型进行逻辑性分组,开发人员使用命名空间来方便地定位一个类型。例如,System.Text命名空间定义了一组执行字符串处理的类型,而System.IO命名空间定义了一组I/O操作的类型。下面构造一个System.IO。FileStream对象和一个System.Text。StringBuilder对象:public sealed class Program{ public static void Main(){ System.IO.FileStream fs = new System.IO.FileStream(…); System.... 阅读全文

4.2 类型转换

2011-12-08 10:33 by iRead, 302 阅读, 收藏,
摘要: CLR最重要的特征之一就是类型安全性。在运行时,CLR总是知道一个对象是什么类型。调用GetType方法,总是知道一个对象确切的类型是什么。由于这个方法是非虚方法,所以一个类型不可能伪装成另一个类型。例如:Employee类型不能重写GetType方法,并返回一个SuperHero类型。 开发人员经常需要将一个对象从一种类型转换为其他各种类型。CLR允许将一个对象转换为它的(实际)类型或者它的任何基类型。每种编程语言都规定了开发人员具体如何进行这种转型操作。例如,C#不要求任何特殊语法即可将一个对象转换为它的任何基类型,因为向基类型的转换被认为是一种安全的隐式转换。然而,将对象转换为它的... 阅读全文

4.2.1 使用C#的is和as操作符来转型

2011-12-08 10:21 by iRead, 348 阅读, 收藏,
摘要: 在C#语言中进行类型转换的另一种方式是使用is操作符。is检查一个对象是否兼容于指定的类型,并返回一个Boolean值:true或false。注意is操作符永远不会抛出异常,以下代码进行了演示: Object o = new Object(); Boolean b1 = (o is Object); //b1为true. Boolean b2 = (o is Employee);//b2为false. 如果对象引用是null,is操作符总是返回false,因为没有可检查其类型的对象。is操作符通常像下面这样使用: if (o is Employee){ Employee e... 阅读全文

4.1 所有类型都从System.Object派生

2011-12-07 10:27 by iRead, 333 阅读, 收藏,
摘要: “运行时”要求每个类型最终都从System.Object类型派生。换言之,以下两个类型定义是完全一致的: 由于所有类型最终都从System.Object派生,所以可以保证每个类型的每个对象都有一组最基本的方法。具体地说,System.Object类提供了如表4.1所示的公共实例方法。 表4-1 System.Object的公共方法公共方法说明Equals如果两个对象具有相同的值,就返回true。欲知该方法的详情,请参见5.3.2节“对象相等性和同一性”GetHashCode返回对象的值的一个哈希码。如果某个类型的对象要在一个哈希表中作为key使用,该类型应使用这个方法。方法应该为不同的... 阅读全文

第4章 类型基础

2011-12-07 10:25 by iRead, 216 阅读, 收藏,
摘要: 本章内容:所有类型都从System.Object派生类型转换命名空间和程序集运行时的相互关系 本章将讲述使用类型和CLR时需要掌握的一些基础知识。具体地说,本章将讨论所有类型都具有的一组最基本的行为。还将讨论类型安全性、命名空间、程序集以及如何将对象从一种类型转换成另一种类型。本章最后会解释类型、对象、线程栈和托管堆在运行时的相互联系。 阅读全文

3.8 “运行时”如何解析类型引用

2011-12-07 10:17 by iRead, 522 阅读, 收藏,
摘要: 第2章开头展示了一下源代码:public sealed class Program{ public static void Main() { System.Console.WriteLine("Hi"); }} 编译这段代码并生成一个程序集,假定为Program.exe。运行这个应用程序时,CLR会加载并初始化它。然后,CLR读取程序集的CLR头,查找标识了应用程序入口方法(Main)的MethodDefToken。然后,CLR会检索MethodDef元数据表,找到该方法的IL代码在文件中的偏移量,把这些IL代码JIT(just-in-time,”即时”)... 阅读全文

3.7 私有部署强命名程序集

2011-12-06 10:40 by iRead, 339 阅读, 收藏,
摘要: 在GAC中安装程序集具有几个方面的优势。GAC使程序集能被多个应用程序共享,从而减少了总体的物理内存占用。除此之外,很容易将程序集的一个新版本部署到GAC中,并让所有应用程序都通过一个发布者策略(本章稍后讲述)来使用这个新版本。GAC还实现了对程序集的多个版本的并行管理。然而,GAC通过会受到严密保护,只有管理员才能在其中安装程序集。此外,一旦向GAC中安装,就会违反“简单复制式部署”这一宗旨。 虽然强命名程序集能安装到GAC中,但这一点绝非强求。事实上,只有在程序集要由多个应用程序共享的时候,才应该将程序集部署到GAC中。如果程序集不需要共享,就应该以私有方式部署它。私有部署维护了“简... 阅读全文

3.6 延迟签名

2011-12-06 10:31 by iRead, 325 阅读, 收藏,
摘要: 本章前面讲过如何使用SN.exe工具来生成公钥/私钥对。这个工具在生成密钥时,会调用Windows提供的Crypto API。密钥可存储到文件或者其他存储设备中。例如,大企业(比如Microsoft)会将自己的私钥保存到一个硬件设备中,再将这个硬件设备锁到一个保险库中。公司里只有少数几个人才能访问到私钥。这个措施可以防止私钥泄漏,并保证了密钥的完整性。当然,公钥是完全公开的,可以自由分发。 当你准备好对自己的强命名程序集进行打包时,必须使用受到严密保护的私钥对它进行签名。然而,在开发和测试程序集时,访问这些受到严密保护的私钥可能有些费事儿。有鉴于此,.NET Framework提供了对延... 阅读全文

3.5 强命名程序集能防范篡改

2011-12-05 15:02 by iRead, 359 阅读, 收藏,
摘要: 用一个私钥对程序集进行签名,可保证程序集是由对应公钥的持有者生成的。程序集安装到GAC时,系统对包含清单的那个文件的内容进行哈希处理,并将哈希值与PE文件中嵌入的RSA数字签名进行比较(在用公钥解除了对它的签名之后)。如果两个值完全一致,表明文件的内容未被篡改,可保证你拿到的公钥与发布者的私钥是对应的。除此之外,系统还会对程序集的其他文件的内容进行哈希处理,并将哈希值与清单文件的FileDef表中存储的哈希值进行比较。任何一个哈希值不匹配,表明程序集至少有一个文件被篡改,程序集将无法安装到GAC。重要提示:这个机制只能保证一个文件的内容没有被篡改,但无法告诉你发布者是谁,除非你对生成公钥的.. 阅读全文

3.4 在生成的程序集中引用一个强命名程序集

2011-12-05 14:55 by iRead, 524 阅读, 收藏,
摘要: 任何时候生成一个程序集,程序集都会包含对其他强命名程序集的引用。之所以这样说,是因为System.Object是在MSCorLib.dll中定义的,后者就是一个强命名的程序集。除此之外,在你的程序集中,还可以引用由Microsoft、第三方厂商或你自己公司开发的其他强命名程序集。第2章介绍了如何使用CSC.exe的/reference编译器开关来指定想要引用的程序集文件名。如果文件名是一个完整路径,CSC.exe会加载指定的文件,并根据它的元数据来生成程序集。如第2章所述,如果指定的是一个不包含路径的文件名,CSC.exe会尝试在以下目录查找程序集(按所列顺序):工作目录包含CSC.exe.. 阅读全文

3.3 全局程序集缓存

2011-12-02 16:44 by iRead, 2178 阅读, 收藏,
摘要: 了解了如何创建强命名程序集之后,接着让我们讨论如何部署它,以及CLR如何利用特定的信息来定位并加载程序集。 如果一个程序集要由多个应用程序访问,必须把它放到一个已知的目录中,而且CLR在检测到对该程序集的一个引用时,必须知道自动检查该目录。这个已知的位置称为全局程序集缓存(Global Assembly Cache,GAC)。对于.NET3.5和以前的版本,GAC通常位于以下目录(假定Windows安装到C:\Windows目录): C:\Windows\Assembly 对于.NET4.0,GAC位于以下目录: C:\Windows\Microsoft.NET\Assembly GA... 阅读全文

3.2 为程序集分配强名称

2011-12-02 11:27 by iRead, 680 阅读, 收藏,
摘要: 要由多个应用程序访问的程序集必须放到一个已知的目录中。另外,检测到对该程序集的一个引用时,CLR必须能自动检查这个目录。但现在的问题是:两个(或者更多)公司可能生成具有相同文件名的程序集。所以,假如两个程序集都复制到同一个移植的目录,最后一个安装的就是“老大”,造成正在使用旧程序集的所有应用程序都无法正常工作(这正是Windows的“DLL Hell”现象的根源,因为共享DLL全部被复制到System32目录中)。 显然,只根据文件名来区分程序集是不够的。CLR必须提供对程序集进行唯一性标识的机制。这正是“强命名程序集”的来历。一个强命名的程序集具有4个重要attribute,它们共同对... 阅读全文

3.9.1 发布者策略管理

2011-11-28 14:06 by iRead, 418 阅读, 收藏,
摘要: 在上一节的例子中,是有程序集的发布者将程序集的一个新版本发送给管理员,后者安装程序集,并手动编辑应用程序或机器的XML配置文件。通常,发布者希望在修复了程序集的一个bug之后,能采取一种容易的方式将新程序集打包并分发给所有用户。但是,发布者还需要一种方式告诉没有用户的CLR使用新的程序集版本,而不要继续使用旧版本。当然,可以指示每个用户手动修改应用程序或机器的XML配置文件,但这相当不方便,而且很容易出错。因为,发布者需要用一种方式来创建策略信息,而新程序集安装到用户机器上时,就会安装这些策略信息。本节将描述程序集的发布者如何创建这种策略信息。 假定你是一个程序集的发布者,而且刚刚创建了... 阅读全文