原文:[CLR via C#]7. 常量和字段

7.1 常量

  常量(constant)是一个特殊的值,它是一个从不变化的值。

  在定义常量时,它的值必须在编译时确定。确定之后,编译器将常量的值保存到程序集的元数据中。这就意味着只能为编译器认定的基元类型定义常量。
 
  C#是允许定义一个非基元类型的常量变量(constant variable),但这个值应设为null。
public sealed class SomeType {
//SomeType不是基元类型,但C#允许定义
//值为null的这种类型的一个常量变量
public const SomeType Empty = null;
}

  由于常量的值从不变化,所以常量总是被视为类型定义的一部分。所以,常量是静态成员,而不是实例成员。定义常量将导致创建元数据。

  代码引用一个常量符号时,编译器会在定义常量的程序集的元数据中查找该符号,提取常量的值,并将值嵌入生成的IL代码中。由于常量的值直接嵌入代码中,所以运行时不需要为常量额外分配内存。
  除此之外,不能获取常量的地址,也不能以传递引用的方式传递常量。
 
  基于上一条,假如A程序集只依赖于B程序集中的常量,那么编译后,即使删除B程序集,A程序集也不会受到影响,也是能找到B程序集中定义的常量值。
 
7.2 字段
 
  字段(field)是一种数据成员,其中容纳了一个值类型的实例或者对一个引用类型的引用。
 
  下表总结了应用于字段的修饰符:
CLR术语 C#术语 说明
Static static 这种字段是类型状态的一部分,而不是对象状态的一部分
Instance 默认 这种字段与类型的一个实例关联,而不是与类型本身关联
InitOnly readOnly 这种字段只能由一个构造器方法中的代码写入
Volatile volatile 看到访问这种字段的代码,编译器、CLR或硬件就不会执行一些"线程不安全"的优化措施
   
  CLR支持类型(静态)字段和实例(非静态)字段。对于类型字段,用于容纳字段数据的动态内存是在类型对象中分配的,而类型对象是在类型加载到一个AppDomain时创建的。什么时候要将类型加载到一个AppDimain中呢?通常是在引用了该类型的任何方法首次进行JIT编译的时候。
 
  对于实例字段,用于容纳字段数据的动态内存则是在构造类型的一个实例时分配的。
 
  由于字段存储在动态内存中,所有它们的值在运行时才能获取。字段还解决了常量存在的版本控制的问题。此外,字段可以是任何数据类型。
 
  CLR支持readonly字段和read/write字段。大多数字段是read/write字段,这意味着在代码执行过程中,字段可以多次改变。但是,readonly字段只能在一个构造器方法中写入(在这有人会有疑问,我可以直接定义readonly的值啊?比如private readonly int ss = 123;不必从构造器方法中写入?这问题,以后会提到)。注意,可以利用反射来修改readonly字段。
 
  下面演示了如何定义一个与类型本身关联的readonly静态字段和读/写静态字段。另外还定义了read/wite静态字段,以及readonly和read/write实例字段。
public sealed class SomeType {
// 这是一个静态readonly字段:在运行时对Random类进行初始化
// 它的值会被计算并存储到内存中
public static readonly Random s_random = new Random(); // 这是一个静态read/write字段
private static Int32 s_numberOfWrites = ; // 这是一个实例readonly字段
public readonly String Pathname = "Untitled"; // 这是一个实例read/write字段
private System.IO.FileStream m_fs; public SomeType(String pathname) {
// 这行修改只读字段Pathname
// 由于是在构造其中,所有可以进行修改
this.Pathname = pathname;
} public String DoSomething() {
// 该行读写静态read/write字段
s_numberOfWrites = s_numberOfWrites + ; // 这行读取readonly实例字段
return Pathname;
}
}

  在上述代码中,许多字段都是内联初始化的。C#允许使用内联初始化语法来初始化类的常量、read/write字段和readonly字段。在后面会讲到,C#实际是在构造器中对字段进行初始化的,字段的内联初始化只是一种语法上的简化而已。另外,在C#中使用内联初始化,有一些性能问题需要考虑,这些以后会着重讲解。

  注意,当某个字段是引用类型,并且该字段标记为readonly时,那么不可改变的引用,而非字段引用的值。
internal static class ReadOnlyReferences {
public sealed class AType {
// InvalidChars总是引用同一个数组对象
public static readonly Char[] InvalidChars = new Char[] { 'A', 'B', 'C' };
} public sealed class AnotherType {
public static void M() {
// 下面三行代码是合法的,可通过编译并运行
// 修改InvalidChars数组中的字符
AType.InvalidChars[] = 'X';
AType.InvalidChars[] = 'Y';
AType.InvalidChars[] = 'Z'; // 下一行代码是非法的,无法通过编译
// 因为不能让InvalidChars 引用别的什么东西
//AType.InvalidChars = new Char[] { 'X', 'Y', 'Z' };
}
}
}

[CLR via C#]7. 常量和字段的更多相关文章

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

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

  2. CLR类型设计之类型之常量和字段

             前言 孔子说:温故而知新,可以为师矣.所以对于学习过的知识要多复习,并且每一次复习都要尽可能的去扩展,而不是书本上的几句理论知识.很多人都喜欢分享自己的学习内容,记录下生活的点点滴滴 ...

  3. clr via c#读书笔记五:常量和字段

    1.常量是值从不变化的符号.只能定义编译器识别的基元类型的常量.如:Boolean,Char,Byte,SByte,Int16,UInt16,Int32,UInt32,Int64,Single,Dou ...

  4. [Clr via C#读书笔记]Cp7常量和字段

    Cp7常量和字段 常量 常量在编译的时候必须确定,只能一编译器认定的基元类型.被视为静态,不需要static:直接嵌入IL中: 区别ReadOnly 只能在构造的时候初始化,内联初始化. 字段 数据成 ...

  5. C#常量和字段以及各种方法的语法总结

    目录 一. 常量和字段.... 1 1. 常量.... 1 2.字段.... 1 二.方法.... 2 1.实例构造器和类(引用类型).... 2 2.实例构造器和结构(值类型).... 2 3.类型 ...

  6. C#基础之类型和成员基础以及常量、字段、属性

    首先吐糟一下今天杭州的天气,真是太热了!虽然没有妹子跟我约会,但宅在方寸大的窝里,也是烦躁不已! 接上一篇<C#基础之基本类型> 类型和成员基础 在C#中,一个类型内部可以定义多种成员:常 ...

  7. 常量,字段,构造方法 调试 ms 源代码 一个C#二维码图片识别的Demo 近期ASP.NET问题汇总及对应的解决办法 c# chart控件柱状图,改变柱子宽度 使用C#创建Windows服务 C#服务端判断客户端socket是否已断开的方法 线程 线程池 Task .NET 单元测试的利剑——模拟框架Moq

    常量,字段,构造方法   常量 1.什么是常量 ​ 常量是值从不变化的符号,在编译之前值就必须确定.编译后,常量值会保存到程序集元数据中.所以,常量必须是编译器识别的基元类型的常量,如:Boolean ...

  8. C#学习笔记——常量、字段以及事件

    一 常量与字段 (一) 常量 常量总是被视为静态成员,而不是实例成员.定义常量将导致创建元数据.代码引用一个常量时,编译器会在定义常量的程序集的元数据中查找该符号,提取常量的值,并将值嵌入IL中.由于 ...

  9. [一]class 文件浅析 .class文件格式详解 字段方法属性常量池字段 class文件属性表 数据类型 数据结构

    前言概述  本文旨在讲解class文件的整体结构信息,阅读本文后应该可以完整的了解class文件的格式以及各个部分的逻辑组成含义   class文件包含了java虚拟机指令集 和  符号表   以及若 ...

随机推荐

  1. c++ 学籍管理系统v 1.0

    #include<iostream> #include <string> #include<conio.h> using namespace std; class ...

  2. apache本地多域配置(wampserver本地多域配置)

    当我们在当地发展.通常在浏览器中输入 http://localhost/项目目录名 测试Web文件,你有没有想过在本地浏览器中,输入自己设定的名字进入项目目录,名相关的问题. 比方我想配置一个主域名w ...

  3. 在win7在结构cocos2d-x v3.2rc0开发环境(For Android)

    cocos2d-x 这是现在比较流行的游戏引擎., 因此.本文的目的在于教导新手怎样在win7下建立cocos2dx开发环境, 截止本文,cocos2dx的最新版本号为 v3.2rc0版,我将如果您的 ...

  4. Python网络01 原始Python服务器

    原文:Python网络01 原始Python服务器 作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 之前我的Python教程中有人 ...

  5. C++虚函数表分析

    在<<反C++>>文中提到的 VC6.0 上的编译器支持一个d1reportAllClassLayout的开关, 能够输出全部对象的内存布局信息, 我自己经常使用vs2005来 ...

  6. POJ 3979 分数减法【数学问题的探讨】

    将a/b和c/d简化一下就可以了 分数加减法 Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 12588   Accepted ...

  7. Oracle解锁的相关操作(转)

    当某个数据库用户在数据库中插入.更新.删除一个表的数据,或者增加一个表的主键时或者表的索引时,常常会出现ora-00054:resource busy and acquire with nowait ...

  8. php session 读写锁

    php session 读写锁 先看一个样例,功能: 1.点击页面中一个button,ajax运行php,php中用session记录运行到哪一步. 2.使用ajax轮询还有一个php,获取sessi ...

  9. ThreadLocal的内存泄露(转)

    ThreadLocal的目的就是为每一个使用ThreadLocal的线程都提供一个值,让该值和使用它的线程绑定,当然每一个线程都可以独立地改变它绑定的值.如果需要隔离多个线程之间的共享冲突,可以使用T ...

  10. Android Drawable绘图学习笔记(转)

    如何获取 res 中的资源 数据包package:android.content.res 主要类:Resources Android SDK中的简介:Class for accessing an ap ...