CLR via C#, 4th -- 【设计类型】 -- 第7章常量和字段
7.1常量
常量是值从不变化的符号。
定义常量符号时,它的值必须能在编译时确定。确定后,编译器将常量值保存到程序集元数据中。这意味着只能定义编译器识别的基元类型的常量。
在C#中,以下类型是基元类型,可用于定义常量:Boolean,Char,Byte,SByte,Int16,UInt16,Int32,UInt32,Int64,UInt64,Single,Double,Decimal和String。然而,C也允许定义非基元类型的常量变量(constant variable),前提是把值设为null:
using System; public sealed class SomeType { // SomeType is not a primitive type but C# does allow // a constant variable of this type to be set to 'null'. public const SomeType Empty = null; }
由于常量值从不变化,所以常量总是被视为类型定义的一部分。换言之,常量总是被视为静态成员,而不是实例成员。定义常量将导致创建元数据。
代码引用常量符号时,编译器在定义常量的程序集的元数据中查找该符号,提取常量的值,将值嵌入生成的IL代码中。由于常量的值直接嵌入代码,所以在运行时不需要为常量分配任何内存。除此之外,不能获取常量的地址,也不能以传引用的方式传递常量。这些限制意味着常量不能很好地支持跨程序集的版本控制。
using System; public sealed class SomeLibraryType { // NOTE: C# doesn't allow you to specify static for constants // because constants are always implicitly static. public const Int32 MaxEntriesInList = 50; }
using System; public sealed class Program { public static void Main() { Console.WriteLine("Max entries supported in list: " + SomeLibraryType.MaxEntriesInList); } }
注意代码引用了在SomeLibraryType类中定义的MaxEntriesInList常量。编译器生成应用程序代码时,会注意到MaxEntriesInList是值为50的常量符号,所以会将Int32值50嵌入应用程序的IL代码,如下所示。事实上,在生成了应用程序程序集之后,运行时根本不会加载DLL程序集,可以把它从磁盘上删除。
如果开发人员将常量MaxEntriesInList的值更改为1000,并且只是重新生成DLL程序集,那么应用程序程序集不受任何影响。应用程序要获得新值,也必须重新编译。
如果希望在运行时从一个程序集中提取另一个程序集中的值,那么不应该使用常量,而应该使用readonly字段
7.2字段
字段是一种数据成员,其中容纳了一个值类型的实例或者对一个引用类型的引用。
CLR支持类型(静态)字段和实例(非静态)字段。如果是类型字段,容纳字段数据所需的动态内存是在类型对象中分配的,而类型对象是在类型加载到一个AppDomain时创建的(参见第22章"CLR寄宿和AppDomain")。那么,什么时候将类型加载到一个AppDomain中呢?这通常是在引用了该类型的任何方法首次进行JT编译的时候。如果是实例字段,容纳字段数据所需的动态内存是在构造类型的实例时分配的。
|
CLR Term |
C# Term |
Description |
|
Static |
static |
The field is part of the type’s state, as opposed to being part of an object’s state. |
|
Instance |
(default) |
The field is associated with an instance of the type, not the type itself. |
|
InitOnly |
readonly |
The field can be written to only by code contained in a constructor method. |
|
Volatile |
volatile |
Code that accessed the field is not subject to some thread-unsafe optimizations that may be performed by the compiler, the CLR, or by hardware. Only the following types can be marked volatile : all reference types, Single , Boolean, Byte, SByte, Int16, UInt16 , Int32, UInt32 , Char, and all enumerated types with an underlying type of Byte, SByte, Int16, UInt16 , Int32, or UInt32 . Volatile fields are discussed in Chapter 29, “Primitive Thread Synchronization Constructs. |
CLR支持readonly字段和read/write字段。大多数字段都是read/write字段,意味着在代码执行过程中,字段值可多次改变。但readonly字段只能在构造器方法中写入。(构造器方法只能调用一次,即对象首次创建时。)编译器和验证机制确保readonly字段不会被构造器以外的任何方法写入。注意,可利用反射来修改readonly字段。
using System; public sealed class SomeLibraryType { // The static is required to associate the field with the type. public static readonly Int32 MaxEntriesInList = 50; }
假设DLL程序集的开发人员将50改为1000,并重新生成程序集。当应用程序代码重新执行时,它将自动提取字段的新值1000。应用程序不需要重新生成,可以直接运行(尽管性能会受到一点影响),要注意的是,当前假定的是DLL程序集的新版本没有进行强命名,而且应用程序的版本策略是让CLR加载这个新版本。
public sealed class SomeType { // This is a static readonly field; its value is calculated and // stored in memory when this class is initialized at run time. public static readonly Random s_random = new Random(); // This is a static read/write field. private static Int32 s_numberOfWrites = 0; // This is an instance readonly field. public readonly String Pathname = "Untitled"; // This is an instance read/write field. private System.IO.FileStream m_fs; public SomeType(String pathname) { // This line changes a read only field. // This is OK because the code is in a constructor. this.Pathname = pathname; } public String DoSomething() { // This line reads and writes to the static read/write field. s_numberOfWrites = s_numberOfWrites + 1; // This line reads the read only instance field. return Pathname; } }
浙公网安备 33010602011771号