第九章 参数
可选参数和命名参数
- 可选参数:在方法声明中为参数指定默认值,在调用方法时,如果不提供相应可选参数的值,将会使用默认值
- 命名参数:在调用方法时通过指定参数名称来传递参数值,而不是按照参数在方法签名中的顺序,并且可以跳过一些可选参数
- 规则和原则
- 可以为方法,构造器方法,有参属性,委托定义的参数指定默认值
- 有默认值的参数必须在无默认值的参数后(可变数量参数的情况除外)
- 默认值必须是编译时能确认的常量值(基元类型,枚举类型,可为null的引用类型),可以用default和new关键字来表达
- 不要重命名参数变量,不要更改参数默认值
- ref和out关键字标识的参数不能设置默认值
隐式类型的局部变量
- 在编译器可以推断局部变量的类型时,可以用var声明(编译器能自动察觉返回类型的变化并自动更改参数类型)(不可以var a = null)
- 不可以用var声明参数类型
- 注意var与dynamic的区别:var只能声明局部变量,必须显式初始化;dynamic可以用于局部变量,字段和属性,无需初始化;表达式不能转成var,但可以转成dynamic
以引用方式传递参数
- 默认情况下所有参数都是值传递:值类型参数传递值的副本,引用类型参数传递对象引用(能够修改对象本身)
- 用out和ref以引用方式传递参数,CLR不区分两个关键字,两者生成的IL代码相同(所以两个重载方法只有out和ref的区别是不合法的,C#要求调用时必须指定out和ref是为了调用方法时更清晰了解方法的意图)
- out在返回前必须向这个值写入
- ref在调用前必须初始化参数的值
- 引用类型必须与方法签名中声明的类型相同
可变数量的参数
使用params关键字(只能应用于最后一个参数),指定一个数组容纳不定数量的实参(可以是Object[]以实现任意数量,任意类型的参数)
参数和返回类型的设计规范
- 声明参数类型时,尽量指定最弱的类型(使用IEnumerable<T>要比List<T>更灵活,因为IEnumerable<T>可以通过数据,List<T>,String等)
- 声明返回类型时,尽量指定最强的类型(使用FileStream要比Stream灵活,因为FileStream可以视作Stream对象,反之不行)
常量性
CLR不允许将方法或参数声明为常量(实际上C++中的常量参数也可以通过取地址和强制类型转换绕过常量不可更改的限制)
第十章 属性
无参属性
- 属性都有名称和类型(不能是void),不能重载。可以有选择的实现get和set方法,以将所有的字段设为private来保护数据(通过属性来访问和修改字段)
- 编译器在属性名前自动附加get_和set_生成方法名
- 自动实现的属性:声明一个属性而不提供get/set方法的实现,C#会自动生成一个私有字段
- 不能显式的初始化
- 无法反序列化
- 不能添加断点
- 一些属性和字段的误区
- 属性可以只读和只写,字段一般总是可读可写的(readonly除外)
- 属性访问可以抛出异常,字段不会
- 属性不能作为out/ref参数传给方法,字段可以
- 属性的访问方法可能较为耗时,字段总是立即访问
- 连续多次调用,属性可能返回不同值(例DateTime.Now),字段每次返回相同值
- 属性的访问可能造成对象状态的改变,字段不会
- 对属性返回对象的修改可能作用不到原始对象上,字段返回的引用总是指向原始对象状态的一部分
对象和集合初始化器
若要构造一个对象并设置对象的一些公共属性或字段,可以使用的特殊语法,例:
var somtype = new SomeType() { xx = xxx, yy = yyy}
若有无参构造器,还可以省略小括号()
主要用来提升代码可读性
匿名类型
声明一个不可变的元组类型,例:
var a = new { xx = xxx, yy = yyy }
编译器会自动生成私有字段及其对应的共有只读属性,并生成一个构造器,重写Object的Equals,GetHashCode,ToString方法
由于重写了GetHashCode以及字段是只读的,所以可以在哈希表中作为键使用
定义多个相同结构的匿名类型,只会创建一个匿名类型定义
可以创建类似的System.Tuple类型(泛型,通过Item1、Item2等访问属性)
有参属性(在C#中称为索引器,可以看作对[]运算符的重载)
- 索引器至少有一个参数,参数和返回类型可以是除了void以外的其他类型
- 索引器默认生成的方法名为get_Item和set_Item(可以用IndexerName特性改变)
- C#允许一个类定义多个索引器(参数集需要不同)
属性访问器方法的性能
简单的get和set代码,JIT编译器会将代码内联(将代码直接编译到调用它的方法中),基本没有性能损失(编译好的方法会变大,但是访问器的代码一般来说比较少)
属性访问器的可访问性
可以为get和set设置不同的可访问性(一般get-public,set-protected);属性本身的可访问性只能是二者限制更大的那个
泛型属性访问器方法
C#不允许属性使用泛型参数,此时应该定义方法而不是属性