原文:[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. LeetCode Solutions : Swap Nodes in Pairs

    Given a linked list, swap every two adjacent nodes and return its head. For example, Given 1->2-& ...

  2. 从Java到C++——从union到VARIANT与CComVariant的深层剖析

    我第一次用VARIANT和CComVariant的时候全然不明确它是怎么回事,为它什么数据类型都能够存放.什么数据都能够被能够初始化?里面究竟是怎么实现的?感觉又奇妙又迷惑!我们在上一节中讲了unio ...

  3. centos下一个bash: XXX: command not found解决方案

    最近想centos通过做Android工程建设.配置jdk和Android sdk后,也同时/etc/profile将java和Android环境变量配置成,但它不能像windows 在相同,直接使用 ...

  4. Oracle Product Hub / Product Lifecycle Management / Product Information Management / Advanced Produc

    In this Document   Goal   Solution   1. Master List showing sample code for APIs in Product Data Hub ...

  5. 查看SQLServer 代理作业的历史信息

    原文:查看SQLServer 代理作业的历史信息 不敢说众所周知,但是大部分人都应该知道SQLServer的代理作业情况都存储在SQLServer5大系统数据库(master/msdb/model/t ...

  6. LeetCode——Climbing Stairs

    You are climbing a stair case. It takes n steps to reach to the top. Each time you can either climb ...

  7. bsh for android : 北京

    beanshell : bjtime.bsh source("/sdcard/com.googlecode.bshforandroid/extras/bsh/android.bsh" ...

  8. HDU 1950 Bridging signals (DP)

    职务地址:HDU 1950 这题是求最长上升序列,可是普通的最长上升序列求法时间复杂度是O(n*n).显然会超时.于是便学了一种O(n*logn)的方法.也非常好理解. 感觉还用到了一点贪心的思想. ...

  9. [WebView其中一项研究]:Web Apps基本介绍

    今天,我们开始了解WebView,以及Web Apps发展,从主要内容Android实际的例子来解释正式文件和后续. (博客地址:http://blog.csdn.net/developer_jian ...

  10. ProgressDialog(三)——代号为中心的屏幕上显示ProgressDialog(ProgressBar)

    MainActivity如下面: package cc.testprogressdialog; import android.os.Bundle; import android.view.Gravit ...