• [C#]跨模块的可选参数与常量注意事项


    假设某个DLL里有这么一个类:

    1 // Lib.dll
    2 public class Lib
    3 {
    4     public const string VERSION = "1.0";
    5     public static void PrintVersion(string version = "1.0")
    6     {
    7         Console.WriteLine(version);
    8     }
    9 }

    然后有这么个调用方:

    // Program.exe
    class Program
    {
        static void Main()
        {
            Console.WriteLine(Lib.VERSION);
            Lib.PrintVersion();
            Console.Read();
        }
    }

    Program.exe的运行结果是显而易见的:

    1.0
    1.0

    过了一段时间,Lib更新版本了:

    // Lib.dll
    public class Lib
    {
        public const string VERSION = "2.0";
        public static void PrintVersion(string version = "2.0")
        {
            Console.WriteLine(version);
        }
    }

    把Lib.dll重新编译确保Program.exe引用了最新的DLL,然后再运行Program.exe,结果:

    1.0
    1.0

    重新编译Program.exe以后再次运行:

    2.0
    2.0

    发现问题了吧,调用方必须重新编译才能确保可选参数和常量的值是最新的。

    原因是这样的:

     1 .method private hidebysig static 
     2     void Main () cil managed 
     3 {
     4     // Method begins at RVA 0x2050
     5     // Code size 30 (0x1e)
     6     .maxstack 8
     7     .entrypoint
     8 
     9     IL_0000: ldstr "1.0"
    10     IL_0005: call void [mscorlib]System.Console::WriteLine(string)
    11     IL_000a: ldstr "1.0"
    12     IL_000f: call void CsConsole.Program/Lib::PrintVersion(string)
    13     IL_0014: call int32 [mscorlib]System.Console::Read()
    14     IL_0019: pop
    15     IL_001a: ret
    16 }

    这是第一次编译之后Program.Main方法的IL,Lib.VERSION完全被编译成了字面量"1.0"(第10行),而第11行的"1.0"是来自(第一次编译时的)Lib.dll的元数据。
    显然,不管怎么更新Lib的代码,只要不重新编译Program,这里的两个值就没办法得到更新,而实际的生产环境中经常没法保证调用方会被重新编译。
    至于为什么要这样编译,我是这么理解的。
    常量的值是在常量池里待着的,通过类的成员去取值显然是不如直接从常量池取来得方便快捷。
    而可选参数的实现方式,则是在编译时提前进行判断与赋值,节省了运行时的时间。

    解决方法
    对于可选参数,《CLR via C#》建议的方法是这样的:

    1 public static void PrintVersion(string version = null)
    2 {
    3     if (version == null)
    4     {
    5         version = "1.0";
    6     }
    7     Console.WriteLine(version);
    8 }

    很显然这段代码的行为与之前相比是有变化的,但是大多数情况下确实可以解决值更新的问题,但是也带来了运行时效率的问题。

    对于常量,从我使用的示例就可以看出来,版本号这类的值不应该定义为常量,使用readonly可以达到目的。
    而对于真正的常量,则不应该轻易地变更它的值。

    最后嘛,Java虽然没有const,但是static final也有同样的表现,同样需要注意这一点。

    要是觉得本文还算有点意思就在右下角点个推荐呗~
  • 相关阅读:
    Save the problem!
    Divisiblity of Differences
    定个小目标
    Faulty Robot
    反片语 uva 156(map的使用
    Input is terminated by EOF.
    uva10815 andy的字典(set的应用)
    uva-101 搬砖问题(不定长数组vector的使用)
    回文串uva401(清简出风尘)
    WERTYU (善用常量数组
  • 原文地址:https://www.cnblogs.com/vd630/p/4559118.html
Copyright © 2020-2023  润新知