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

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

然后有这么个调用方:

// 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

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

原因是这样的:

 .method private hidebysig static
void Main () cil managed
{
// Method begins at RVA 0x2050
// Code size 30 (0x1e)
.maxstack
.entrypoint IL_0000: ldstr "1.0"
IL_0005: call void [mscorlib]System.Console::WriteLine(string)
IL_000a: ldstr "1.0"
IL_000f: call void CsConsole.Program/Lib::PrintVersion(string)
IL_0014: call int32 [mscorlib]System.Console::Read()
IL_0019: pop
IL_001a: ret
}

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

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

 public static void PrintVersion(string version = null)
{
if (version == null)
{
version = "1.0";
}
Console.WriteLine(version);
}

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

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

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

[C#]跨模块的可选参数与常量注意事项的更多相关文章

  1. (1)ES6中let,const,对象冻结,跨模块常量,新增的全局对象介绍

    1.let声明变量,var声明变量,而const声明的常量 2.let与var的区别 let可以让变量长期驻扎在内存当作 let的作用域是分块[ {快1  {快2 }  }每个大括号表示一个独立的块 ...

  2. es65 跨模块常量

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  3. 小知识:C#可选参数的一个陷阱

    一.背景: 互联网行业,为了降低程序维护.升级的部署风险,往往会将程序拆分成很多项目,编译成多个dll部署,这样发布的时候,只需要部署修改过的dll即可.   二.问题: 有一个函数,在很多个地方被使 ...

  4. 通过Queue的构造函数的可选参数maxsize来设定队列长度

    创建一个"队列"对象 import Queuemyqueue = Queue.Queue(maxsize = 10) Queue.Queue类即是一个队列的同步实现.队列长度可为无 ...

  5. c# 方法参数(传值,传引用,ref,out,params,可选参数,命名参数)

       一.方法参数的类型----值类型和引用类型 当方法传递的参数是值类型时,变量的栈数据会完整地复制到目标参数中即实参和形参中的数据相同但存放在内存的不同位置.所以,在目标方法中对形参所做的更改不会 ...

  6. ThinkPHP实现跨模块调用操作方法概述

    ThinkPHP实现跨模块调用操作方法概述 投稿:shichen2014 字体:[增加 减小] 类型:转载   使用 $this 可以调用当前模块内的方法,但是很多情况下经常会在当前模块中调用其他模块 ...

  7. C# 方法的可选参数、命名参数

    原文 http://www.cnblogs.com/lonelyxmas/admin/EditPosts.aspx?opt=1 C#方法的可选参数是.net 4.0最新提出的新的功能,对应简单的重载可 ...

  8. 可选参数、命名参数、.NET的特殊类型、特性

    1.可选参数和命名参数    1.1可选参数        语法:            [修饰符] 返回类型 方法名(必选参数n,可选参数n)        注意:            1.必选参 ...

  9. C# 4.0 可选参数 和 命名参数

    可选参数 可选参数是 C# 4.0 提出来的,当我们调用方法,不给这个参数(可选参数)赋值时,它会使用我们定义的默认值. 需要注意的是: (1)可选参数必须位于所有必选参数的后面: (2)可选参数必须 ...

随机推荐

  1. python单引号和双引号的区别

    今天在网上爬虫的时候,很奇怪的发现python的字符串既可以用双引号又可以用单引号,于是就上网百度了一下原因. 原来由于字符串中有可能既有双引号又有单引号,例如: 字符串:demo'1'. 这时候就可 ...

  2. set 集合数据类型

    set 数据类型 set 与列表类似,区别在于 set 不能包含重复的值. In [1]: a_list = ['a', 'b', 'c', 'b', 'd', 'm', 'n', 'n'] In [ ...

  3. KVM到KVM之v2v迁移

    1.源KVM虚拟主机node1 (1).查看源KVM虚拟主机上的虚拟机列表,本文计划将oeltest01虚拟机迁移到其它KVM虚拟主机中. (2).查看oeltest01虚拟机磁盘文件位置/data/ ...

  4. [总结帖] 后端MVC V.S. 前端MVVM

    Web编年史: Web1.0 —— 静态页面.简单预处理语言草案:PHP.JSP.ASP Web2.0 —— 企业级架构.一站式解决方案(MVC):J2EE.Spring.Asp.net Web2.5 ...

  5. 如何将Ajax请求从异步改为同步

    Ajax请求默认的都是异步的 如果想同步 async设置为false就可以(默认是true) var html = $.ajax({   url: "some.PHP",   as ...

  6. spring中scope的prototype与singleton区别

    最近在研究单例模式,突然想起项目中以下配置,scope="singleton" 和 scope="prototype"到底有何区别呢?以下做下简要分析. < ...

  7. JS框架设计之对象类型判断一种子模块

    Javascript有两套数据类型,一套是基础数据类型,一套是对象数据类型.基础数据类型包括5种基本数据类型,分别是null,bool,undefined,number,string,也叫简单数据类型 ...

  8. 《LeetBook》leetcode题解(14):Longest Common Prefix[E]

    我现在在做一个叫<leetbook>的免费开源书项目,力求提供最易懂的中文思路,目前把解题思路都同步更新到gitbook上了,需要的同学可以去看看 书的地址:https://hk029.g ...

  9. 发布aar到jcenter

    准备工作 创建bintray账号; 在https://bintray.com/, 选择如下图中,方框内"Sign Up Here": 选择合适的方式,创建账号: 新建仓库: Add ...

  10. MySQL的连接方式

    连接MySQL操作是连接进程和MySQL数据库实例进行通信.从开发的角度来说,本质上是进程通信.常用的进程通信方式有管道.命名管道.命名字.TCP/IP套接字.Unix域名套接字.MySQL提供的连接 ...