假设某个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. 比较有用的php代码片段

    一 从网页中提取关键词 $meta = get_meta_tags('http://www.emoticode.net/'); $keywords = $meta['keywords']; // Sp ...

  2. “全栈2019”Java第八十八章:接口中嵌套接口的应用场景

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  3. “全栈2019”Java第八十五章:实现接口中的嵌套接口

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  4. LOJ#2882. 「JOISC 2014 Day4」两个人的星座(计算几何)

    题面 传送门 题解 我们发现如果两个三角形相离,那么这两个三角形一定存在两条公切线 那么我们可以\(O(n^2)\)枚举其中一条公切线,然后可以暴力\(O(n^3)\)计算 怎么优化呢?我们可以枚举一 ...

  5. 两个for循环效率,哪个高

    在多重循环中,如果有可能,应当将最长的循环放在最内层,最短的循环放在最外层,以减少CPU跨切循环层的次数.

  6. mxonline实战6 , 忘记用户密码时进行重置

        对应github地址:密码重置   原理:  1. 一个需要输入用户邮箱和注册码的密码忘记页面 2. 点击提交后,用户邮箱收到一个邮件,包含重置密码的链接 3. 点击链接进入密码重置页面   ...

  7. SpringMvc渲染视图

    这篇博文讨论的问题是从ModelAndView如何渲染到页面. 首先要知道每个请求处理完之后都会返回一个ModelAndView对象. 这里我分6种情况来分析,代表6种返回类型: ModelAndVi ...

  8. vscode 本地调试nodejs

    1.首先通过node-inspect插件可以debug   nodejs ,先起nodejs服务,再启用node-inpector服务 安装调试器 npm install -g node-inspec ...

  9. leetcode-733-Flood Fill

    题目描述: An image is represented by a 2-D array of integers, each integer representing the pixel value ...

  10. windows 下 pyinstaller distutils not included with latest virtualenv (16.4.0)

    起因 因为windows下python3.7.2内置venv虚拟环境下pyinstaller错误问题,切换virtualenv但是发现最新版本又有其他问题 - -! ModuleNotFoundErr ...