可选参数和命名参数
不多说,上代码,自然懂
class Program
{
static void Main(string[] args)
{
var troy = new Troy();
troy.HelloWorld();//此时b和c都为0
troy.HelloWorld(,);//此时b为2,c为0,以上两个为可选参数的玩法 troy.HelloWorld(a: , b: );//命名参数玩法
troy.HelloWorld(b: , a: );//即使顺序打乱,效果也是一样
}
}
public class Troy {
public void HelloWorld(int a, int b = ,int c=default(int)) {//这里b和c参数就是可选参数
//注意default(int)这种玩法,表示int的默认值。
//我是第一次知道这种用法,然而非常推崇这样的玩法,因为可以有效减少你代码中的魔法数字。也许你认为0这种不算魔法数字,然而我认为能让代码更简单易懂一点点也是非常有必要的。
//就算不用魔法数字,那么default(DateTime)去判断DateTime值是否为默认值,是不是比new Datetime()更好一点呢?
}
}
默认参数实际上在C#编译器编译过后就向该参数应用特性OptionalAttribute和DefaultParameterValueAttribute。并不是CLR支持的,而是C#特有的。
这两个东西看上去都那么美好,然而美好的东西并不一定真的好。
推荐用命令参数,然而不要为了调换顺序而调换顺序。实际上对VS这么强大的工具而言,命名参数只有在参数非常多的时候才有用。
如果你的参数太多,那么其实更应该考虑缩小一下参数的数量。可以考虑提取一个参数对象,传值的时候传这个参数对象就好了。
虽然我非常喜欢用默认参数,实际上它也确实很好用,特别对于重载而言,你有的时候根本没必要去写两个函数。
然而这实际上也是个坑点。
如果你和我一样喜欢用,你团队的人也喜欢用。那么最后你会发现,这个东西总是会让函数内部充满了各种各样的分支,你的参数列表也会越来越长。o(︶︿︶)o 唉
如果你不懂那么可以看一下我写的一个代码维护小故事,请想象一下你在维护的是一个业务复杂的大型系统,那么接下来的节奏会经常发生。
//最开始Troy写了一个打招呼的函数
public void 打招呼()
{
Console.WriteLine("你好");
//下面还有一系列握手,微笑之类的操作
}
//后来老板说国际化,也要能英文打招呼.你选择了默认参数,并且为了保证以前的代码顺利运行,默认为false
public void 打招呼(bool IsEnglish=false)
{
if (IsEnglish)
{
Console.WriteLine("Hello");
}
else {
Console.WriteLine("你好");
}
//下面还有一系列握手,微笑之类的操作
}
//到了上面那一步也是OK的,然而过了两天,老板跟大牛说有的老外有可能不握手,他选择拥抱。于是大牛改代码:
public void 打招呼(bool IsEnglish = false, bool 是否选择拥抱 = false)
{
if (IsEnglish)
{
Console.WriteLine("Hello");
}
else {
Console.WriteLine("你好");
}
if (是否选择拥抱)
{
Console.WriteLine("拥抱");
}
else {
Console.WriteLine("握手");
}
//下面还有一系列微笑之类的操作
}
当然即使到现在,以上的代码看起来也仅仅只是分支增多而已,然而请你设身处地去想一下,如果这个系统的业务很复杂,如果后面还有需求,如果不仅仅只是一个Console.WriteLine这么简单的操作。
等到第四次去修改的时候,新来的项目组成员小菜已经没得选了。首先他不熟悉业务,不敢乱改,他甚至可能熟悉也懒得改,因为改起来已经很麻烦了(不要太相信你的队友,我就是这样的懒人(☆_☆)),那么这个时候他只能默默选择再加一个默认参数。
有第四个,肯定就会有第五个,每次想要重构感觉难度越来越大,心里越来越虚,只好随大流去加默认参数,经过大家一起努力,这段代码已经差不多10个分支了,大家都不敢改了。
如果从一开始不选择加默认参数,而是多写一个重载函数,将公共部分提炼出来,那么你觉得还会有这样的问题吗?
然而并没有什么鸟用,即使是我了解的这么清楚,我经常会觉得我在加第三次默认参数完全没有任何问题,偷点懒赶紧下班啦,代码依然能看,等我下次回来改的时候,我发现默认参数已经变成5个了。o(︶︿︶)o 唉
out和ref的故事
CLR不区分out和ref,生成的IL代码一模一样,只是元数据会有个bit值加以区分。
out表示传递的是引用类型,然而他并不需要调用的时候这个参数就初始化完毕,且要求函数执行完毕的时候out参数必须被写入过。
ref要求调用的时候这个参数就初始化完毕,且不要求函数执行完毕的时候ref参数必须被写入过。
实际上在C里面这个东西就是指针,我们这里来讲这东西传递的就是值的地址。
如果有大的值类型的传参,比如一个大型struct。
那么用这种方法传参只会传一个地址值,而不是把每个struct里面的值都压到栈中。
另外不要因为ref比out好用就不用out,out可以明确你这个参数一定会传值出来,读代码更容易。
pramas传递可变数量的参数
这个网上一大堆,我用得最多的就是String.Format方法,可以参考这个来了解。
不过要注意这个会有性能损失,因为传递的pramas一维数组,实际上是分配在堆空间中的,初始化啊,垃圾回收啊,都会有影响。
参数和返回类型的设计规范
声明方法的参数类型时,应该按照最低接口类型,更强的接口类型,基类,派生类从高到低的优先级来声明类型。
因为用接口或者基类会让你的方法更加灵活,可以选择的余地更大。
而返回类型正好相反,防止受限于特定类型。
以上是作者讲的,有道理,但是具体情况具体分析,难道要我们都去声明object,那一切都OK了?
作者的意思显然不是如此。我认为你可以在第一次的时候去选择最适合的强类型参数,但是下一次有一个类似的函数时,而两个参数间有一个共同的接口或者基类,那么是否可以考虑一下将参数的类型弱化呢?这样明显可以让代码更灵活。
但是不要因此直接就用个object之类的,不要过度设计,永远用目前最满足需求的那个,不要把未来全部考虑完全,因为你根本预测不到未来。
【C#进阶系列】09 关于参数的故事的更多相关文章
-
JavaScript进阶系列04,函数参数个数不确定情况下的解决方案
本篇主要体验函数参数个数不确定情况下的一个解决方案.先来看一段使用函数作为参数进行计算的实例. var calculate = function(x, y, fn) { return fn(x, y) ...
-
C#进阶系列——WebApi 接口参数不再困惑:传参详解
前言:还记得刚使用WebApi那会儿,被它的传参机制折腾了好久,查阅了半天资料.如今,使用WebApi也有段时间了,今天就记录下API接口传参的一些方式方法,算是一个笔记,也希望能帮初学者少走弯路.本 ...
-
【转】C#进阶系列——WebApi 接口参数不再困惑:传参详解
原文地址:http://www.cnblogs.com/landeanfen/archive/2016/04/06/5337072.html 阅读目录 一.get请求 1.基础类型参数 2.实体作为参 ...
-
[转]C#进阶系列——WebApi 接口参数不再困惑:传参详解
本文转自:http://www.cnblogs.com/landeanfen/p/5337072.html#_label1_2 阅读目录 一.get请求 1.基础类型参数 2.实体作为参数 3.数组作 ...
-
sC#进阶系列——WebApi 接口参数不再困惑:传参详解
原文:http://www.cnblogs.com/landeanfen/p/5337072.html 一.get请求 对于取数据,我们使用最多的应该就是get请求了吧.下面通过几个示例看看我们的ge ...
-
JavaScript进阶系列05,事件的执行时机, 使用addEventListener为元素同时注册多个事件,事件参数
本篇体验JavaScript事件的基本面,包括: ■ 事件必须在页面元素加载之后起效■ 点击事件的一个简单例子■ 为元素注册多个点击事件■ 获取事件参数 ■ 跨浏览器事件处理 □ 事件必须在页面元素加 ...
-
JavaScript进阶系列02,函数作为参数以及在数组中的应用
有时候,把函数作为参数可以让代码更简洁. var calculator = { calculate: function(x, y, fn) { return fn(x, y); } }; var su ...
-
JavaScript进阶系列01,函数的声明,函数参数,函数闭包
本篇主要体验JavaScript函数的声明.函数参数以及函数闭包. □ 函数的声明 ※ 声明全局函数 通常这样声明函数: function doSth() { alert("可以在任何时候调 ...
-
C#进阶系列 ---- 《CLR via C#》
[C#进阶系列]30 学习总结 [C#进阶系列]29 混合线程同步构造 [C#进阶系列]28 基元线程同步构造 [C#进阶系列]27 I/O限制的异步操作 [C#进阶系列]26 计算限制的异步操作 ...
随机推荐
-
unity 协程
StartCoroutine在unity3d的帮助中叫做协程,意思就是启动一个辅助的线程. 在C#中直接有Thread这个线程,但是在unity中有些元素是不能操作的.这个时候可以使用协程来完成. 使 ...
-
Android中View的绘制过程 onMeasure方法简述 附有自定义View例子
Android中View的绘制过程 onMeasure方法简述 附有自定义View例子 Android中View的绘制过程 当Activity获得焦点时,它将被要求绘制自己的布局,Android fr ...
-
memcached学习笔记3--telnet操作memcached
方式: 一.telnet访问memcached缓存系统(主要用于教学,不讨论) telnet 127.0.0.1 11211 => telnet IP地址 端口号 //往Memcache ...
-
打开PDF文件弹出阅读未加标签文档的解决方法
在“高级”菜单的“辅助工具”选中“设置助手”,然后点选“设置屏幕阅读器选项”,下一步之后,将“忽略已加标签文档的阅读顺序”和“添加标签到文档之前进行确认”(有的版本显示的是“为文档加标签前确认”)前面 ...
-
iOS 指南针的制作 附带源码
iOS 指南针的制作 附带源码 代码下载地址: http://vdisk.weibo.com/s/HK4yE http://pan.baidu.com/share/link?shareid=7 ...
-
mysql默认安装目录说明
MySQL安装完成后不象SQL Server默认安装在一个目录,它的数据库文件.配置文件和命令文件分别在不同的目录,了解这些目录非常重要,尤其对于Linux的初学者,因为 Linux本身的目录结构就比 ...
-
String的split方法,你真的懂吗
String的split方法相信大家都不陌生,或多或少都用过它将字符串转成一个数组,但是就是这样一个简单的方法,里面也有一个不得不注意.不深不浅的小坑. 本地测试代码如下图所示: 图1 大家会发现sp ...
-
mysql性能调整三板斧
大意是,用2/8原则,多快好省的解决大部分事情.所以三板斧,仅限整体调整,不牵扯具体细节. 1.innodb 使用innodb引擎 2.innodb_buffer_pool 调整和innodb有关的参 ...
-
《Mysql 字符集》
一:什么是字符集呢? - 引用书中的例子:同样是大熊猫,在大陆叫熊猫,在*叫猫熊,在美国叫Panda,要到了非洲,可能都不知道叫啥(于是就乱码了). - 在例子之后引入字符集的概念:字符集就是指符号 ...
-
ES6数组的新增功能,还是很强大的好多地方用的到
map,reduce,filter ,forEach 废话不多说代码走起 map 映射 就是一个对一个 /* 比如:学生成绩分数对应及格不及格 也可以做一些算数运算 */ let chengji ...