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 read­only 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 read­only 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;   
   }  
}
posted @ 2019-12-06 21:41  FH1004322  阅读(110)  评论(0)    收藏  举报