假设某个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. 【OCP 12c】最新CUUG OCP-071考试题库(62题)

    62.(13-17)choose the best answer: You need to list the employees in DEPARTMENT_ID 30 in a single row ...

  2. django 视图 使用orm values_list()方法获取 指定的 多个字段的数据

    from .models import UserInfodata_set = UserInfo.objects.all().values_list("user_name", &qu ...

  3. jmeter进行https协议的测试

    一.HTTPS和HTTP的区别     超文本传输协议HTTP协议被用于在Web浏览器和网站服务器之间传递信息.HTTP协议以明文方式发送内容,不提供任何方式的数据加密,如果攻击者截取了Web浏览器和 ...

  4. SpringMvc @RequestMapping原理

    讲这个之前,我们得先知道在SpringMvc启动时,会加载所有的Bean类,就是加了@Controller,@Component等组件标识的类,然后会把@RequestMapping的方法也加入到一个 ...

  5. QuantLib 金融计算——基本组件之 DayCounter 类

    目录 QuantLib 金融计算--基本组件之 DayCounter 类 DayCounter 对象的构造 一些常用的成员函数 如果未做特别说明,文中的程序都是 Python3 代码. QuantLi ...

  6. 【转载】Analysis Service Tabular Model #003 Multidimensional Model VS Tabular Model 我们该如何选择?

    由于Multidimensional Model 和 Tabular Model 并不能互相转换, 所以在项目之初就应该要考虑好选择哪一种模型进行开发. 以下只是一些建议: Licensing 许可和 ...

  7. 【转载】SSAS-MDX#001 - MDX 基本结构

    1. MDX 的基本结构 - MDX 的基本结构有三种: Members, Tuple 和 Set    2. Members - 指的是维度树上的一个节点, 这里有一点需要指出, 量度也是一个特殊的 ...

  8. cool kickass

    I can stay like this alllllllllll daaaaaaaaayyyyyy.

  9. 用Docker构建分布式Redis集群

    [编者的话]本文介绍了如何使用Docker搭建Redis集群,很多读者都在问Docker能带来哪些实质性的好处,我想本文就是一个很好的例子.不使用Docker你也可以搭建Redis集群,那使用Dock ...

  10. (Android 即时通讯) [悬赏],无论是谁发现一个漏洞奖励人民币1000元!

    悬赏,无论是谁发现一个漏洞奖励人民币1000元!   3Q Android 手机版即时通讯系统正式推出,可与电脑版 地灵(http://im.yunxunmi.com) 即时通讯系统互通!  适用于: ...