查询语法(query syntax)的一个用处
循环创建集合|数组时,用查询语法(query syntax)会比普通控制流程的结构好点,
主要体现在:
- 命令式的版本有时非常难以理解,要是没有注释或文档,后续维护人员将要重读整段代码才能进行开发。
- 另外,查询语法比循环结构能提供更具组合性的API。查询语法将很自然的把代码分解成小块代码,每一块仅仅对序列中元素进行单一的操作。查询语法的延迟执行模型也让开发者能将这些单一的操作组合成多步操作,且在一次遍历序列时完整执行。
例子:用二元组生成坐标,返回的二元组按照其离远点距离的逆序排列
1 | private static IEnumerable<Tuple<int, int>> ProduceIndices() |
使用具名参数(named parameter)减少重载
1 | static void Test2(string firstName,string secondName) |
理解几个等同性判断之间的关系
当创建自定义类型时(无论是class还是struct),应为类型定义”等同性”的含义。C#提供了4种不同的函数来判断两个对象是否”相等”:
1 | public static bool ReferenceEquals(object left, object right); |
Object.ReferenceEquals()
和Object.Equals()
这两个系统提供的静态方法,永远都不需要重新定义。
Object.ReferenceEquals()
判断的是对象引用,判断的是否拥有同样的对象标识(object identity),所以若将一个值类型与它自身进行比较,方法返回的是false
,因为值类型会进行装箱操作,造成引用地址不同。
Object.Equals()
对于引用类型默认使用对象标识判断,即跟Object.ReferenceEquals()
一样,但对于值类型,因为System.ValueType重写了Object.Equals()
方法,所以比较的是值是否相等(主要是struct),但System.ValueType是所有值类型的基类,故实现比较时,用的是反射,效率并不高。
综上所述,自定义类型实现自己的比较方法就比较重要了。
1 | public class Student : IEquatable<Student> |
注意,重写Equals
方法时,需要同时重写GetHashCode()
方法,详细可查看条目7。
operator==()
则相对简单。只要创建的是值类型,都必须重定义operator==()
。理由和重写System.ValueType的Equals
是一样的。而引用类型则应该避免重写operator==()
。
运行时常量(readonly)和编译期常量(const)
C#有两种类型的常量:编译期常量和运行时常量。两者有截然不同的行为,使用不当的话,会造成性能问题,如果没法确定,则使用慢点,但能保证正确的运行时常量。
运行时常量使用readonly
关键字声明,编译期常量则使用const
关键字声明:
1 | //声明编译期常量 |
二者最重要的区别在于,readonly
值是运行时解析的,而const
是在生成IL码就已经确定。
const
声明的常量必须不能改变,若改变了,则需要重新编译所有引用的程序集。