[读书笔记]C#学习笔记七: C#4.0中微小改动-可选参数,泛型的可变性
前言
下面就开始总结C#4.0的一些变化了, 也是这本书中最后的一点内容了, 这一部分终于要更新完了. 同时感觉再来读第二遍也有不一样的收获. 今天很嗨的是武汉下雪了,明天周六,一切都是这么美好.哈哈哈.
主要内容有: 可选参数和命名实参, 泛型的可变性, 动态类型
1,可选参数和命名实参
1.1可选参数
可选参数和命名实参就如同一对好基友, 因为它们经常一起使用. 
可选参数重在"可选", 即在调用方法时, 该参数可以明确指定实参, 也可以不指定实参.如下代码:
class Program
{
static void Main()
{
TestMethod(, , "WangMeng");
TestMethod(, );
Console.ReadKey();
} //带有可选参数的方法
static void TestMethod(int x, int y = , string name = "BarryWang")
{
Console.Write("x = {0} y = (1) name = {2};", x, y, name);
}
}
打印结果如下图:
是不是有一种很神奇的感觉? 这就是可选参数的好用之处, 特别是对于一个系统的后期维护很好使用, 在真实的项目中我也使用过这样的用法, 如下例:
在我们做的系统中切换User有SwitchUser(不lougout当前user,然后添加新的user登陆)和TransferUser(logout当前user,然后登陆新的user)两种方式
但是系统又会进行对登陆的user数量进行限制, 而SwitchUser和TransferUser使用的都是同一个限定Check方法,而两种对User的操作方式不同,所以导致TransferUser会出现问题.
这里的解决方案就是仍然使用同一个Check方法,但是给这个Check方法新添加一个可选参数来判断到底是执行的哪个操作, 然后根据不同的操作去做相应的修改.
在使用可选参数时, 需要注意一下几个约束条件:
(1)所有可选参数必须位于必选参数之后.
(2)可选参数的默认值必须为常亮.
(3)参数数组(有params修饰符声明)不能做为可选参数
(4)用ref或out关键字标识的参数不能被设置为可选参数
看到这里我们就可以发现可选参数的最大的优点就是便于系统后期的维护. 其他的优点还有待发现.
1.2命名实参
如果一个系统中有两个可选参数, 而我们想省略掉第一个可选参数怎么办呢? 命名实参这个时候就可以帮助我们了.
class Program
{
static void Main()
{
//省略name参数
TestMethod(, );
//省略y参数和name参数
TestMethod();
//为不分实参指定名称, 通过使用命名实参, 只省略y参数
TestMethod(, name : "WangMeng");
//为所有实参指定名称
TestMethod(x: , y: , name: "Hello");
Console.ReadKey();
} //带有两个可选参数的方法
static void TestMethod(int x, int y = , string name = "BarryWang")
{
Console.WriteLine("x = {0}, y = {1}, name = {2}", x, y, name);
}
}
打印结果如下图:

有了命名实参, 可选参数的变得更加强大了是不是? 哈哈, 确实是这样.
2,泛型的可变性
在C#2.0 中, 泛型并不具备可变性, 这种是指斜变性和逆变性. 而在C#4.0中引入了泛型的协变性和逆变性.
2.1协变性
协变性指的是泛型类型参数可以从一个派生类隐式转化为基类. 大家可以这样记忆: 协变性即和谐(与"协"同音)的变化,
从派生类转换为基类, 就如同所子女长的像父母一样, 听起来非常和谐. 这样就很容易记住协变了.
C#4.0引入out关键字来标记泛型参数, 以示其支持协变性. 为了更好的进行说明, 下面用.Net类苦中的IEnumerable<out T>接口为例做演示:
class Program
{
static void Main()
{
//初始化泛型实例
List<object> listObject = new List<object>();
List<string> listStrs = new List<string>(); listObject.AddRange(listStrs);//成功
listStrs.AddRange(listObject);//失败
}
}
在以上代码中, AddRange方法接收的参数类型为IEnumerable<T>, 该接口的定义为IEnumerable<out T>, 因为其泛型参数有out关键字标识, 
所以IEnumerable<T>泛型的类型参数T支持协变性, 则可将List<string>转化为IEnumerable<string>(这是被继承的协变性支持的. 因为List<T>实现了IEnumerable<T>接口). 
又因为类型参数支持协变性, 所以可以进一步把IEnumerable<string>转化为IEnumerable<object>
2.2逆变性
逆变性指的是泛型类型参数可以从一个基类隐式地转化为派生类,C#4.0引入in关键字来标记泛型参数, 以示其支持逆变性.
下面使用.Net类库中的接口public interface IComparer<in T>为例进行演示:
class Program
{
static void Main(string[] args)
{
List<object> listobject = new List<object>();
List<string> liststrs = new List<string>();
// AddRange方法接收的参数类型为IEnumerable<T> collection
// 下面的代码是传入的是List<string>类型的参数。
// 在MSDN中可以看出这个接口的定义为——IEnumerable<int T>。
// 所以 IEnumerable<T>泛型类型参数T支持协变性,所以可以
// 将List<string>转化为IEnumerable<string>(这个是继承的协变性支持的)
// 又因为这个IEnumerable<in T>接口委托支持协变性,所以可以把IEnumerable<string>转化为——>IEnumerable<object>类型。
// 所以编译器验证的时候就不会出现类型不能转化的错误了。
listobject.AddRange(liststrs); //成功 ////liststrs.AddRange(listobject); // 出错 IComparer<object> objComparer = new TestComparer();
IComparer<string> objComparer2 = new TestComparer(); // List<string>类型的 liststrs变量的sort方法接收的是IComparer<string>类型的参数
// 然而下面代码传入的是 IComparer<object>这个类型的参数,要编译成功的话,必须能够转化为IComparer<string>这个类型
// 正是因为IComparer<in T>泛型接口支持逆变,所以支持object转化为string类型
// 所以下面的这行代码可以编译通过,在.Net 4.0之前的版本肯定会编译错误,
// 大家可以把项目的目标框架改为.Net Framework 3.5或者更加低级的版本
// 这样下面这行代码就会出现编译错误,因为泛型的协变和逆变是C# 4.0 中新增加的特性,而.Net 4.0对应于C# 4.0。
liststrs.Sort(objComparer); // 正确 // 出错
////listobject.Sort(objComparer2);
}
} public class TestComparer : IComparer<object>
{
public int Compare(object obj1,object obj2)
{
return obj1.ToString().CompareTo(obj2.ToString());
}
}
在以上代码中, listStrs变量的Sort应接收IComparer<string>类型的参数, 虽然传入的实参是IComparer<objcet>类型, 
但因为IComparer<in T>泛型接口支持逆变, 所以可将object转化为string类型.
2.3协变和逆变的注意事项
(1)只有接口和委托才支持协变和逆变, 类或泛型方法的类型参数都不支持协变和逆变
(2)协变和逆变只适用于引用类型, 值类型不支持协变和逆变(例如List<int>无法转化为IEnumerable<objcet>)
(3)必须显式地用in或out来标记类型参数
(4)委托的可变性不要再多播委托中使用
3,动态类型
在C#4.0中, 微软引入了dynamic管家你来定义动态类型. 当我们使用由dynamic关键字限制的变量时, 编译器并不知道它的类型, 该类型智能在程序运行时才能被确定.
动态类型的定义为: dynamic i = 5;
动态类型和静态类型到底有什么不同呢?
object obj = ;
obj = obj + ;//出现变异错误
dynamic i = ;
i = i + ;
解析:
在以上代码中, 第一行的obj为objec他类型, 而编译器却检测出"+"运算符无法应用于object和int类型. 
要让编译器通过, 我们必须使用强制类型转换, 把object转换为int. 即obj = (int)obj + 10;
但是动态类型的引入到底有什么好处呢? 
1,可以减少强制类型转换的使用. 因为动态类型是在程序运行时才被确定, 使用它可以避免代码进行强制类型转换,从而使代码看起来更加简洁.
2,调用Python等动态语言. 动态类型除了可以减少强制类型转换外, 还可以让我们在C#语言中调用Python这样的动态语言.
这里对动态类型介绍的不多, 主要是介绍了一个dynamic关键字, 如果以后用到再来百度就好了.
PS: 想为自己的文字多增加一点内容, 以后每个帖子后面都会加一些口语小贴士, 这些都是自己平时看过的. 英语真的很重要, 这里不用我多说大家应该都知道的.
口语小贴士:
A fool never learns.
傻瓜永远学不会
A little bird told me.
我听说的
Are you out of your mind?
你疯了吗?
Are you pulling my leg?
你在开我玩笑吗?
As far as I'm concerned.
就我而言
[读书笔记]C#学习笔记七: C#4.0中微小改动-可选参数,泛型的可变性的更多相关文章
- [读书笔记]C#学习笔记一: .Net Framwork
		
前言: 一次偶然的机会 在园子里看到@Learning hard 出版的一本书: <<C#学习笔记>>, 然后买来 一直到现在读完, 感觉很不错, 适合入门, 书中内容是从C ...
 - [读书笔记]C#学习笔记三: C#类型详解..
		
前言 这次分享的主要内容有五个, 分别是值类型和引用类型, 装箱与拆箱,常量与变量,运算符重载,static字段和static构造函数. 后期的分享会针对于C#2.0 3.0 4.0 等新特性进行. ...
 - [读书笔记]C#学习笔记四: C#2.0泛型 可控类型 匿名方法和迭代器
		
前言 C#1.0的委托特性使方法作为其他方法的参数来传递,而C#2.0 中提出的泛型特性则使类型可以被参数化,从而不必再为不同的类型提供特殊版本的实现方法.另外C#2.0还提出了可空类型,匿名方法和迭 ...
 - [读书笔记]C#学习笔记八:StringBuilder与String详解及参数传递问题剖析
		
前言 上次在公司开会时有同事分享windebug的知识, 拿的是string字符串Concat拼接 然后用while(true){}死循环的Demo来讲解.其中有提及string操作大量字符串效率低下 ...
 - [读书笔记]C#学习笔记二: 委托和事件的用法及不同.
		
前言: C#委托是什么 c#中的委托可以理解为函数的一个包装, 它使得C#中的函数可以作为参数来被传递, 这在作用上相当于C++中的函数指针. C++用函数指针获取函数的入口地址, 然后通过这个指针 ...
 - [读书笔记]C#学习笔记六: C#3.0Lambda表达式及Linq解析
		
前言 最早使用到Lambda表达式是因为一个需求:如果一个数组是:int[] s = new int[]{1,3,5,9,14,16,22};例如只想要这个数组中小于15的元素然后重新组装成一个数组或 ...
 - [读书笔记]C#学习笔记五: C#3.0自动属性,匿名属性及扩展方法
		
前言 这一章算是看这本书最大的收获了, Lambda表达式让人用着屡试不爽, C#3.0可谓颠覆了我们的代码编写风格. 因为Lambda所需篇幅挺大, 所以先总结C#3.0智能编译器给我们带来的诸多好 ...
 - 面向小白的JS笔记 - #Codecademy#学习笔记
		
前言 最初浏览过<JavaScript秘密花园>,前一段时间读过一点点<JavaScript语言精粹>和一点点<JavaScript高级程序设计>(一点点是指都只是 ...
 - contiki-main.c 中的process系列函数学习笔记 <contiki学习笔记之六>
		
说明:本文依然依赖于 contiki/platform/native/contiki-main.c 文件. ---------------------------------------------- ...
 
随机推荐
- [2015hdu多校联赛补题]hdu5378 Leader in Tree Land
			
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5378 题意:给你一棵n个结点的有根树.因为是有根树,那么每个结点可以指定以它为根的子树(后面讨论的子树 ...
 - POJ 1511 - Invitation Cards 邻接表 Dijkstra堆优化
			
昨天的题太水了,堆优化跑的不爽,今天换了一个题,1000000个点,1000000条边= = 试一试邻接表 写的过程中遇到了一些问题,由于习惯于把数据结构封装在 struct 里,结果 int [10 ...
 - java核心知识点学习----重点学习线程池ThreadPool
			
线程池是多线程学习中需要重点掌握的. 系统启动一个新线程的成本是比较高的,因为它涉及与操作系统交互.在这种情形下,使用线程池可以很好的提高性能,尤其是当程序中需要创建大量生存期很短暂的线程时,更应该考 ...
 - zlib快速编译脚本
			
zlib允许使用IDE编译生成dll以及静态库,高版本写一个脚本就能轻松的一键编译生成静态/动态lib以及dll文件 以下是一句话编译批处理脚本 nmake -f win32\Makefile.msc ...
 - 揭开HTTP网络协议神秘面纱系列(三)
			
HTTP首部字段有四种类型:通用首部字段,请求首部字段,响应首部字段,实体首部字段. 通用首部字段: 首部字段 说明 Cache-Control 控制缓存的行为 Connection 逐跳首部.连接的 ...
 - Android编译环境搭建(0818-0819)
			
1 在虚拟机VMware上安装64位Ubuntu14.04LTS 首先需要安装虚拟机并激活.然后新建虚拟机,选择使用下载好的Ubuntu镜像.注意需要将光驱改为自己下载的,而不是autoinst.is ...
 - BigDecimal类的加减乘除
			
Java在java.math包中提供的API类BigDecimal,用来对超过16位有效位的数进行精确的运算. 双精度浮点型变量double可以处理16位有效数,但是在实际应用中,需要对更大或者更小的 ...
 - [MOSEK] Mosek求解中遇到的奇葩内存问题
			
在使用mosek优化库的时候,使用http://docs.mosek.com/7.0/capi/MSK_getxx_.html的 MSKrescodee MSK_getxx ( MSKtask_t t ...
 - Verilog之基本算数运算
			
1.加减法 module addsub ( :] dataa, :] datab, input add_sub, // if this is 1, add; else subtract input c ...
 - mysql基本命令(转)
			
连接到本机上的MYSQL.首先打开DOS窗口,然后进入目录mysql\bin,再键入命令mysql -u root -p,回车后提示你输密码.注意用户名前可以有空格也可以没有空格,但是密码前必须没有空 ...