[C#]跨模块的可选参数与常量注意事项
假设某个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)ES6中let,const,对象冻结,跨模块常量,新增的全局对象介绍
1.let声明变量,var声明变量,而const声明的常量 2.let与var的区别 let可以让变量长期驻扎在内存当作 let的作用域是分块[ {快1 {快2 } }每个大括号表示一个独立的块 ...
- es65 跨模块常量
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 小知识:C#可选参数的一个陷阱
一.背景: 互联网行业,为了降低程序维护.升级的部署风险,往往会将程序拆分成很多项目,编译成多个dll部署,这样发布的时候,只需要部署修改过的dll即可. 二.问题: 有一个函数,在很多个地方被使 ...
- 通过Queue的构造函数的可选参数maxsize来设定队列长度
创建一个"队列"对象 import Queuemyqueue = Queue.Queue(maxsize = 10) Queue.Queue类即是一个队列的同步实现.队列长度可为无 ...
- c# 方法参数(传值,传引用,ref,out,params,可选参数,命名参数)
一.方法参数的类型----值类型和引用类型 当方法传递的参数是值类型时,变量的栈数据会完整地复制到目标参数中即实参和形参中的数据相同但存放在内存的不同位置.所以,在目标方法中对形参所做的更改不会 ...
- ThinkPHP实现跨模块调用操作方法概述
ThinkPHP实现跨模块调用操作方法概述 投稿:shichen2014 字体:[增加 减小] 类型:转载 使用 $this 可以调用当前模块内的方法,但是很多情况下经常会在当前模块中调用其他模块 ...
- C# 方法的可选参数、命名参数
原文 http://www.cnblogs.com/lonelyxmas/admin/EditPosts.aspx?opt=1 C#方法的可选参数是.net 4.0最新提出的新的功能,对应简单的重载可 ...
- 可选参数、命名参数、.NET的特殊类型、特性
1.可选参数和命名参数 1.1可选参数 语法: [修饰符] 返回类型 方法名(必选参数n,可选参数n) 注意: 1.必选参 ...
- C# 4.0 可选参数 和 命名参数
可选参数 可选参数是 C# 4.0 提出来的,当我们调用方法,不给这个参数(可选参数)赋值时,它会使用我们定义的默认值. 需要注意的是: (1)可选参数必须位于所有必选参数的后面: (2)可选参数必须 ...
随机推荐
- ASP.net/C#中如何调用动态链接库DLL
动态链接库(也称为DLL,即为“Dynamic Link Library”的缩写)是Microsoft Windows最重要的组成要素之一,打开Windows系统文件夹,你会发现文件夹中有很多DLL文 ...
- CSS3-渐变这个属性2
渐变 有了渐变再也不用去切1px的图再重复了.. -webkit- 是浏览器前缀, 表示特定浏览器对一个属性还在实验阶段, 在这里顺便写下各个浏览器的前缀: chrome/ safari -w ...
- “全栈2019”Java多线程第一章:认识多线程
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- SpringMvc+ajax 实现json格式数据传递
传JSON对象 前端 function test () { var param = {username : "yitop"}; $.ajax({ timeout : 20000, ...
- [总结帖] 后端MVC V.S. 前端MVVM
Web编年史: Web1.0 —— 静态页面.简单预处理语言草案:PHP.JSP.ASP Web2.0 —— 企业级架构.一站式解决方案(MVC):J2EE.Spring.Asp.net Web2.5 ...
- JavaBean简介及使用
JavaBean简介及使用 望星空 https://blog.csdn.net/joywy/article/details/7773503 一.JavaBean简介 JavaBean是使用Jav ...
- Lambda入门
Lambda 来源于微积分数学中的 λ,其涵义是声明为了表达一个函数具体需要什么. Table of contents Introduction 使用 Introduction 什么是Lambda? ...
- 前端视频插件Aliplayer播放器简单使用(基于地址播放)
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http ...
- Optional类
参照: 一篇简单使用介绍 官网详细用法介绍 包含各种例子的cheetsheet 一个封装某个value的容器 一般可用于方法返回值类型,提醒调用方,这个值可能为null,所以需要处理(因为空指针异常是 ...
- 为什么要重写hashcode方法和equals方法
我们可能经常听到说重写equals方法必须重写hashcode方法,这是为什么呢?java中所有的类都是Object的子类,直接上object源码 /* * Copyright (c) 1994, 2 ...