1.命名空间声明:命名空间是一种特殊的分类机制,它将与一个特定功能有关的所有类型都分组到一起。一般将外层命名空间指定为公司名,向内依次是产品名,最后是功能区域,比如Microsoft.Win32.Networking。命名空间中中可以包含句点,这样使得命名空间“显得”层次分明,但是,这只对增强可读性有利,因为编译器认为所有命名空间都在同一个级刷上,
例如 System.Collections.Generics 似乎被包含在 System.Collections 命名空间内部,但是对于编译器来说.它们是两个完生抽立的,完全不同的命名空间。
2.using导入的区别:在文件的顶部放置 using 指令和在命名空间声明的内部的顶部放置 using 指令的区别在于,后者的 using 指令只在你声明的那个命名空间内有效,假如在 Awl.Mlchaelis.EssentialCSharp 命名空间的外部又声明了一个新的命名空间,新的命名空间不会受到 using.System指令的影响。不过,很少会这样写代码,尤其是根据约定,每个文件只应该有一个类型声明。
3.using的别名:利用 using 指令为命名空间或类型取一个别名,别名(alias)是在 using 指令所在的那个范围内可以使用的一个替代名称。之所以要使用别名,两个最常见的原因是:消除同名的两个类型的歧义和缩写一个长名称。
4.程序入口方法:Main()方法支持int返回值,而不是仅仅支持vold返回. 退回值对于Main()声明来说是可选的. 但是,假如使用了它. 程序就可以将一个状态码返回给调用者(调用者可以是一个脚本,也可以是一个批处理文件)。根据约定,一个非零的返回值代表一个错误。虽然所有命令行参数都可以通过一个字符串数组传递给Main(),但我们有时候可能需要从一个不同于Main()的方法中访问那些参数,在这种情况下,可以使用 System.Environment.GetCommandLineArgs()方法,该方法采取和Main(string[] args)将参数传递给Main()一样的方式来返回命令行参数。
5.C#4.0新增:
(1)可选参数:可选参数一定放在所有必须的参数(无默认值的参数)后面。另外,默认值必须是一个常量,或者说必须是编译时能确定的一个值,这-点极大限制了“可选参数”的应用。
如:DirectoryCountLines(string directory,string extension="*.cs")
(2)命名参数:利用命名参盘,调用者可显式指定参数名.并为该参数赋一个值,而不是像以前那样只能依据参数顺序来决定哪个值赋给哪个参数,
如:DisplayGreeting(firstName:"Lucy",lastName:"Jack");
public void DisplayGreeting(string firstName,string middleName=default(string),string lastName=default(string))
{
....省略
}
如果一个方站有大量的参数,而且其中许多都是可选的(访问Microsoft COM库时,这是很常见的-种情况),那么命名参数语法肯定能带来不少便利。但要注意的是,这个便利的代价是牺牲方法接口的灵活性,过去{至少就 C#来说),参数名可以自由更改,不会造成调用代码无法编译的情况. 但在添加了命名参数后,参数名就成为方法接口的一部分,更改名称会导致使用命名参数的代码无法编译。
6.假如方法重载、可选参数和命名参数这几种技术一起使用,我们难以一眼看出最终调用的到底是哪个方法,只有在所有参数(可选参数除外)都恰好有一个对应的实参(不管是根据名称还是位置),而且该实参具有兼容类型的情况下,才说一个调用适用于(兼容于)一个方法。虽然这限制了可调用的方法数量,但还不足以唯一性地标识方法,为了进一步区分方法,编译器只使用调用者显式标识的参数,忽略调用者没有指定的所有可选参数。所以,假如由于一个方法有一个可选参数,造成两个方法都适用,编译器最终选择的将是无可选参数的方法。
编译器从一系列“适用”的方法中选择最终调用的方法时,依据的是哪个方法最具体,只能有一个方法完全匹配调用者传递的参数,所以,该方法总是具有最高优先级。
如:调用者传递的是一个 int,那么接受 double 的方法将优先于接受 object 的方法,这是由于 double 比 object 更具体,如果有多个适用的方法,但无法从中挑选出最具唯一性的,编译器就会报错,指明调用存在歧义。在写理序时,最好是使用显式转型,方便别人理解你想调用哪个目标方法。
7.try-catch-finally的使用:catch由具体到一般,假如在捕捉Exception 的catch 块之后添加了一个具体的 catch 块,编译器就会报告一条警告消息,指出具体的catch块永远都不会执行。没有指定数据类型的 catch 块称为泛化catch块(generic catch block),它等价于获取 object数据类型的catch块。
例如 catch(object exception){ . . . ),由于所有类最终都是从 object派生 , 所以没有数据类型的 catch 块必组出现在最后。泛化的 catch 块很少使用,因为没有办法捕捉有关异常的任何信息。避免使用异常处理来处理预料之中的情况,开发者应当尽量避免为预料之中的情况或者正常的控制流引发异常,异常是专门为了跟踪例外的、事先没有预料到的、而且可能造成严重后果的情况而设计的,为预料之中的情况使用异常,会造成你的代码难以阅读、理解和维护。
C#在抛出异常时会产生些许性能损失,相较干大多数操作都是纳秒级的速度,它可能造成毫秒级的延迟,人们平常注意不到这个延迟,除非异常没有得到处理。
8.catch块中使用throw:可以在一个catch块中重新引发异常(如在catch(ArgumentNullException ex)的实现中,可以包含对throw ex的调用),然后像这样重新引发异常,会将栈追踪重置为重新引发的位置,而不是重用原始引发位置。在一个catch块的内部,假如你重新引发一个不同的异常,那么不仅会重置引发点,还会隐藏原始异常,为了保留原始异常,需要设置新异常的InnerException属性(该属性通常可以通过构造器来赋值)。
9.自定义异常:自定义异常唯一的硬性要求就是它必须从System.Exception或者其某个子类派生,除此之外,在使用自定义异常的时候,还应遵照以下最佳实践。
(1)所有异常都应该使用“Exception”后缀,彰显其用途。
(2)通常,所有异常都应该包含以下3个构造器:无参数构造器、获取一个string参数的构造器、以及同时获取一个字符串和一个内部异常作为参数的构造器。
(3)避免使用深的继承层次结构(一般应该小于5级)。
10.从NET Framework 4 开始,枚举类型也添加了一个TryParse方法。
-------------------------------------------以上内容根据《C#本质论 第三版》进行整理