C#:可选参数的陷阱 [转]

一、背景:

互联网行业,为了降低程序维护、升级的部署风险,往往会将程序拆分成很多项目,编译成多个dll部署,这样发布的时候,只需要部署修改过的dll即可。

二、问题:

有一个函数,在很多个地方被使用:

public fun1(A a ,B b)
{
    //代码主体
}

突然有一天,有的地方调用的时候需要加入一个参数C c,但是又不想其他客户程序有任何变动,可以充分利用.net4.0新增的可选参数特性,这样改:

方法一:使用可选参数

public fun1(A a ,B b,C c = 1)
{
 //代码主体
}

程序修改完后,在本地程序完美运行,将dll发测试。在测试环境,莫名其妙的报错。由于系统分层处理,web站点和Web API分离,错误返回到最上层,已经看不到错误原因,查了老半天日志,终于发现是另外一个dll(未重新编译部署)调用了fun1函数,报找不到方法 fun1的错误。

将程序修改为:

方法二:重载

public fun1(A a ,B b)
{
    fun1(A a ,B b,1);
}
public fun1(A a ,B b,C c)
{
   //代码主体
}

重新发布dll,程序正常运行。

三、初步结论:

将代码修改成可选参数,客户程序需要重新编译,不然客户程序调用的时候会报找不到方法的错误。如果,客户程序太多,为了避免部署所有客户程序的dll,可以用方法二,重载的方法。

四、验证:

方法无默认参数:

方法有可选参数:

客户程序Program编译后的IL:

五、最终结论:

由图可知,客户程序的c#源码虽然没有变,但是编译后的IL中间代码确改变了,调用的函数也传了两个参数,并把函数默认参数888在客户程序声明,使用。

可以得出的结论是,C#可选参数函数其实是在编译的时候,在函数调用的客户程序做了处理,把默认参数传了进去。

六、现在问题来了,大家请思考

微软为什么要这样做呢?为什么不在编译的时候,IL像以下代码一样,做类似重载的处理呢? 这样的话就不用重新编译客户程序了

public fun1(A a ,B b)
{
    fun1(A a ,B b,1);
}
public fun1(A a ,B b,C c)
{
   //代码主体
}

七、网友解答:感谢CYJB和其他网友的热心解答,非常详尽

楼主为方法添加了一个默认参数,那么这个新的方法和之前的方法的签名是完全不同的——对应的参数个数是不同的,所以才会出现找不到方法的错误。.Net 里的可选参数就像 @冲杀 说的,就是一个语法糖,仅仅是编译器在方法调用的时候自动加上了默认参数值而已,编译器完全可能不支持这一功能。

实际上,根据 VS “代码分析”功能,可以发现公共方法使用默认参数,是具有一定风险的,因为:在公共语言规范 (CLS) 中允许方法使用默认参数;但是 CLS 允许编译器忽略为这些参数分配的值。 为忽略默认参数值的编译器编写的代码必须为每个默认参数显式提供变量。 为了跨编程语言维护所需的行为,必须使用提供默认参数的方法重载来替换使用默认参数的方法。完整的文档可以参考 MSDN CA1026:不应使用默认参数

最后,公共方法的确是应该只扩展不修改的,以保持良好的兼容性,楼主应该是没有想到 C# 的默认参数只是一个语法糖,而不慎中招了。而微软为何没有将默认方法实现为多个重载,不是不愿,而是不能。例如下面的方法:

public void TestMethod(int a = 100, int b = 200, int c = 300)
{
// ...
}

如果自动实现为多个重载,显然是不可能的,因为参数的类型都是 int,无法根据参数类型来区分出参数 a、b 和 c,因此微软会完全放弃这个方案。而代码膨胀之类的,可能也在微软的考虑范围内。

时间: 2024-10-28 07:11:10

C#:可选参数的陷阱 [转]的相关文章

c#可选参数的一个陷阱

一.背景: 互联网行业,为了降低部署风险,往往会将程序拆分成很多项目,编译成多个dll部署,这样修改的时候,只需要部署修改过的dll即可. 二.问题: 有一个函数,在很多个地方被使用: public fun1(A a ,B b) { //代码主体 } 突然有一天,有的地方调用的时候需要加入一个参数C c,但是又不想其他客户程序有任何变动,可以这样改: 方法一:使用可选参数 public fun1(A a ,B b,C c = 1) { //代码主体 } 程序修改完后,在本地程序完美运行,将dll

【转】C#具名参数和可选参数

源地址:https://www.cnblogs.com/similar/p/5006705.html 另:可选参数的一个陷阱 参考:https://www.cnblogs.com/still-windows7/p/kexuancanshuxianjing.html 原文地址:https://www.cnblogs.com/haizine/p/10567291.html

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

 一.方法参数的类型----值类型和引用类型 当方法传递的参数是值类型时,变量的栈数据会完整地复制到目标参数中即实参和形参中的数据相同但存放在内存的不同位置.所以,在目标方法中对形参所做的更改不会对调用者的初始变量产生任何影响. 当方法传递的参数是引用类型是,只是将变量的引用复制到目标参数中,实参和形参的引用指向内存中的同一位置.所以,在目标方法中对形参所做的更改会影响调用者的初始变量. 二.一些特殊的方法参数 1.引用参数---ref (使值类型的变量做方法参数时也可以传引用) 一些数据类型(

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

1.可选参数和命名参数    1.1可选参数        语法:            [修饰符] 返回类型 方法名(必选参数n,可选参数n)        注意:            1.必选参数可以不存在,也可以有多个:可选参数可以有1个或多个            2.可选参数必须放在必选参数之后            3.可选参数在定义时需要赋初始值            4.可选参数之间也需要使用,进行分隔            5.调用可选参数时,不能跨参数赋值    1.2命名

可选参数的函数还可以这样设计!

// foo(a[, b], c) // 接收2~3个参数,b是可选参数,如果只传2个参数,b默认为null: function foo(a, b, c) { if (arguments.length === 2) { // 实际拿到的参数是a和b,c为undefined c = b; // 把b赋给c b = null; // b变为默认值 } // ... } php也可以用这个思路来设计哈,不过php没有arguments这样的关键字,可以通过func_num_args方法来获取传入函数内

jQuery form插件的使用--ajaxForm()和ajaxSubmit()的可选参数项对象

一.前提说明 Form Plugin API 里提供了很多有用的方法可以让你轻松的处理表单里的数据和表单的提交过程. 测试环境:部署到Tomcat中的web项目. 二.简单介绍 本文演示的是:jQuery form插件的使用--ajaxForm()和ajaxSubmit()的可选参数项对象 $('#myForm').ajaxForm(function() { $('#output1').html("提交成功!欢迎下次再来!").show(); }); $('#myForm2').sub

MSSQL中存储过程的可选参数的定义和使用

可选参数的存在,可以极大的降低代码的重复冗余.在数据库开发中,也是如此.现在针对MSSQL中存储过程的可选参数的定义和使用进行基本的介绍,留作备忘. #准备工作: 在db_test中建立一张测试表T_test: USE db_test; CREATE TABLE dbo.T_test ( Id  INT  IDENTITY(1,1) NOT NULL ,Name  NVARCHAR(20)  NOT NULL ,Sex  BIT  DEFAULT(0) ); 插入一些数据: INSERT INT

ylbtech-LanguageSamples-NamedAndOptional(命名和可选参数)

ylbtech-Microsoft-CSharpSamples:ylbtech-LanguageSamples-NamedAndOptional(命名和可选参数) 1.A,示例(Sample) 返回顶部 命名和可选参数 C# 4.0 支持命名和可选参数. 此程序演示如何声明一个具有命名和可选参数的方法,以及如何通过使用参数的默认值和通过显式命名特定参数来调用该方法. 1.B,示例代码(Sample Code)返回顶部 1.B.1, Program.cs using System; using S

.net 可选参数和命名参数

可选参数,可以为方法的参数设置默认值,在调用方法的时候,如果不传递参数,就是执行默认参数. class Program { private static void M(int x = 9, string s = "A", DateTime dt = default(DateTime), Guid guid = new Guid()); static void Main(string[] args) { //执行如下方法, M方法会根据参数从左往右匹配. //guid: 就会根据这个名称