C#《CLR Via C# 第3版》笔记之(七) – const和readonly

C#中不时用const或者readonly来定义不可改变常量,那么怎么着使用它们啊?

主要内容:

  • const和readonly的区别
  • readonly的补偿表达

1. const和readonly的区别

重点的分别在于 const是在编译时确定值的,readonly是在运行时确定值的。

之所以,用const修饰的字段,必须在概念的时候就赋值,否则编译器报错。

而readonly修饰的字段除了能够在概念时赋值以外,仍能在构造函数中赋值。

证实的代码如下:

using System;

namespace Test7
{
    public class CLRviaCSharp_7
    {
        const string cValue = "const";
        readonly string rValue;

        public CLRviaCSharp_7()
        {
            rValue = "readonly";
        }

        static void Main(string[] args)
        {
            CLRviaCSharp_7 test7 = new CLRviaCSharp_7();

            Console.WriteLine("cValue=" + CLRviaCSharp_7.cValue);
            Console.WriteLine("rValue=" + test7.rValue);

            Console.ReadKey(true);
        }
    }
}

编译后用ILSpy查看IL代码如下:

.class public auto ansi beforefieldinit CLRviaCSharp_7
    extends object
{
    // Fields
    .field private static literal string cValue = "const"
    .field private initonly string rValue

    // Methods
    .method public hidebysig specialname rtspecialname 
        instance void .ctor () cil managed 
    {
        // Method begins at RVA 0x20c7
        // Code size 21 (0x15)
        .maxstack 8

        IL_0000: ldarg.0
        IL_0001: call instance void object::.ctor()
        IL_0006: nop
        IL_0007: nop
        IL_0008: ldarg.0
        IL_0009: ldstr "readonly"
        IL_000e: stfld string class Test7.CLRviaCSharp_7::rValue
        IL_0013: nop
        IL_0014: ret
    } // End of method CLRviaCSharp_7..ctor

    .method private static hidebysig 
        void Main (
            string[] args
        ) cil managed 
    {
        // Method begins at RVA 0x20e0
        // Code size 48 (0x30)
        .maxstack 2
        .entrypoint
        .locals init (
            [0] class Test7.CLRviaCSharp_7 test7
        )

        IL_0000: nop
        IL_0001: newobj instance void Test7.CLRviaCSharp_7::.ctor()
        IL_0006: stloc.0
        IL_0007: ldstr "cValue=const"
        IL_000c: call void [mscorlib]System.Console::WriteLine(string)
        IL_0011: nop
        IL_0012: ldstr "rValue="
        IL_0017: ldloc.0
        IL_0018: ldfld string class Test7.CLRviaCSharp_7::rValue
        IL_001d: call string string::Concat(string, string)
        IL_0022: call void [mscorlib]System.Console::WriteLine(string)
        IL_0027: nop
        IL_0028: ldc.i4.1
        IL_0029: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey(bool)
        IL_002e: pop
        IL_002f: ret
    } // End of method CLRviaCSharp_7.Main

} // End of class Test7.CLRviaCSharp_7

从Main函数中的IL_0007,我们得以见到,编译时就曾经将
cValue替换为字符串”const”了,所以在Main函数中看不出使用了字段cValue

从Main函数中的IL_0018,大家可以观望,运行时才读取
rValue的值,将其拼接到输出的字符串中。

2. readonly的填补表明

const和readonly纵然都足以定义常量,可是出于readonly是在运行时才确定值的,所以比const尤其灵敏。

本来,readonly的特性肯定比const稍逊。(具体相差多少还一贯不考试过)

readonly与const相比较,使用时须要小心2点。

2.1. readonly的字段可以通过反射来修改

现实参见以下代码

using System;
using System.Reflection;

namespace Test7
{
    public class CLRviaCSharp_7
    {
        static void Main(string[] args)
        {
            ChangeReadonlyClass cr = new ChangeReadonlyClass();
            Console.WriteLine("before change, rValue=" + cr.rValue);

            // 利用反射来改变ChangeReadonlyClass中readonly字段的值
            FieldInfo fi =  typeof(ChangeReadonlyClass).GetField("rValue");
            fi.SetValue(cr, "rValue has changed");
            Console.WriteLine("after  change, rValue=" + cr.rValue);

            Console.ReadKey(true);
        }
    }

    public class ChangeReadonlyClass
    {
        public readonly string rValue;

        public ChangeReadonlyClass()
        {
            rValue = "ChangeReadonlyClass's readonly field";
        }

    }
}

运转结果如下:

C# 1

2.2. readonly的字段为引用类型时,不可变更的是援引,而不是援引的对象。

证实代码如下:

using System;

namespace Test7
{
    public class CLRviaCSharp_7
    {
        public static readonly Char[] rValues = new Char[] { 'X', 'Y', 'Z' };

        static void Main(string[] args)
        {
            // 修改引用的对象,可以成功修改并运行
            rValues[0] = 'A';
            rValues[1] = 'B';
            rValues[2] = 'C';

            // 修改引用本身,无法编译
            rValues = new Char[] { 'A', 'B', 'C' };

            Console.ReadKey(true);
        }
    }
}

readonly并不像const这样是相对的常量。大家在行使其灵活性的还要,也应留神它恐怕带来的副作用。

相关文章