C#代码规范
一、文件命名
1 文件名
文件名统一使用帕斯卡命名法,以C#类名命名,拓展名小写。
示例:
GameManager.cs
2 文件注释
每个文件头须包含注释说明,文件头位置指的是文件最开始处,位于using指令引用命名空间或程序集之前。
示例:
//--------------------------------------------------------------------------------
// Copyright (C) 2015-2016 taixihuase
// 版权所有
//
// 文件名:GameStatusMachine.cs
//
// 文件功能描述:
//
// 游戏状态机基类脚本,用于读写全局游戏状态,发送游戏状态以调控游戏场景切换等
//
// 创建标识:taixihuase 20150327
//
// 修改标识:taixihuase 20150328
// 修改描述:
// 在游戏状态枚举GameStatus中添加枚举值LoginStatus,表示登录状态
//
// 修改标识:
// 修改描述:
//
//-------------------------------------------------------------------------------- using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
文件头注释遵循以下规则。
规则1:以空内容的注释或者内容为一行短横杆的注释起行,也以相同的注释结尾。
规则2:注明版权,Copyright (C),加上最早版本创作或发布的年份以及最新版本的年份,再加上版权持有者名称,“版权所有”四个字可另起一行或者接在同一行。
规则3:文件头注释中的文件功能仅需简述。
规则4:创建标识或修改标识由创建者或修改者及日期组成。
规则5:同一天内同一人多次对文件修改只需做一个修改标识,同一天内不同人对文件进行修改则需另做一个修改标识。
规则6:注释统一用双左斜杆,不要用C风格注释,避免杂乱注释如一行星号填充空位,保持注释整洁。
规则7:必要时,在多项注释内容段之间使用内容为空的注释,而非使用空行。
二、代码风格
1 列宽
编写代码时切换到屏幕高分辨率,以1440*900分辨率下Visual Studio 2013为例,在代码缩放为100%时,代码控制在110列以内。
2 换行
当表达式超出或即将超出列宽时,遵循以下换行规则。
规则1:在操作符前换行。
规则2:在逗号后换行,优先于规则1。
规则3:使用IDE自带的换行,而非手动换行,此规则优先级最高。
3 缩进
每行开头使用一个Tab,占4个字符长度。不要直接敲入4个空格,而应使用Tab键。但不能使用Tab制表符,而应在IDE中设置缩进大小为4,以及转换Tab。以Visual Studio 2013为例,方法如下:
工具->选项->文本编辑器->C#->制表符->插入空格
优先使用IDE自带的缩进功能,无论使用哪个IDE,都应先设置好IDE的缩进选项为插入4个空格。
4 空行
使用空行将逻辑相关的代码进行分块,提高代码的可阅读性。
空行的使用遵循以下规则。
规则1:在类以上级别之间使用两个空行。
1. 接口与接口、类与类、接口与类的定义之间。
2. 枚举与接口、枚举与类的定义之间。
3. 命名空间与命名空间之间。
4. 文件头注释末行与后面的代码之间。
规则2:在类内级别之间使用一个空行。
1. 字段与字段、方法与方法、属性与属性、方法与属性、方法与字段之间。
2. 字段声明与语句之间。
3. 方法中不同逻辑块之间。
4. 方法中返回语句与其他语句之间。
5. 属性与跟该属性不相对应的字段之间。
6. 注释与其他由多条非注释语句组成、共同处理某一逻辑的代码段之间。
规则3:在类内紧密关联内容之间不使用空行。
1. 注释与其他注释之间。
2. 属性与跟该属性相关联的字段之间。
3. 注释与跟该注释对应的字段、属性、方法、接口、类及命名空间。
示例:
// 这是一个C#代码规范命名空间
namespace CSharpStyleGuide
{
// 这是一个接口
interface INothing
{
void Nothing();
} // interface INothing // 这是第一个示例类
class FirstSampleClass
{
private static string className = "FirstSampleClass"; private int property; // 这是一个私有字段,这里用了行尾注释允许的两种情况之一,另一种与右括号关联
public int Property
{
get { return property; }
set { property = value; }
} public int AnotherProperty { get; set; } // 默认构造函数
// 带一个缺省参数value,默认值为0
// 该注释仅供空行使用规则参考,方法的注释应采用之后介绍的文档注释
FirstSampleClass(int value = )
{
Property = value;
} public int GetValue()
{
// 显示提示信息 Console.WriteLine("这个类的类名为" + className);
Console.WriteLine("这个类存储的值大小为:"); // 单行显示值
Console.WriteLine(Property); return Property;
} public virtual void SetValue(int value)
{
this.Property = value;
}
} // class FirstSampleClass // 在这个类内设置主方法入口
class Program
{
static void Main(string[] args)
{
// 暂时不做任何事
}
} // class Program
} // namespace CSharpStyleGuide
5 空格
优先使用IDE如Visual Studio 2013等自带的缩进功能来填补空格,手动空格遵循以下规则。
规则1:关键字及左括号之间用空格隔开。
示例:
while (true)
规则2:方法名与左括号之间不使用空格。
示例:
ShowSample();
规则3:多个参数用逗号隔开,每个逗号后添加一个空格。
示例:
CompareValue(lhs, rhs);
规则4:除了.和->之外,二元运算符与其操作数之间用空格隔开,一元运算符不用空格隔开。
示例:
a += b;
a = a / (a + b);
a++;
规则5:语句中的表达式用空格隔开。
示例:
for (int i = ; i < ; ++i)
规则6:注释左斜杆后与注释的内容之间用空格隔开
示例:
// 这句注释前有个空格
6 括号
优先使用IDE的括号对齐功能,其次在安排括号时遵循下述规则。
圆括号:使用圆括号首先需遵循使用空格的规则,其次,在没有必要的情况下,不要在return语句使用圆括号。
示例:
return ERROR;
花括号:使用时遵循以下的要求。
规则1:左花括号放在关键字或方法名的下一行并与之对齐。
规则2:左花括号与右花括号对齐。
规则3:除了在属性定义时使用的花括号之外,其余花括号单独成行,不与任何语句并列一行。
规则4:if、else、while、do等语句后必须使用花括号,即使内部为空或仅有一条语句,这样便于扩展内容和快速搜索。
规则5:当代码块较长(将近或者超出一屏幕),此时建议在右花括号后添加注释,以便于找到对应的入口条件,注释与右花括号之间空出一格
示例:
while (true)
{
if (isOk)
{
return OK;
}
else
{
// Do Something here. return ERROR;
} // isOK is False
} // true
三、程序注释
1 注释规则
除了文件头注释遵循特定的规则外,在编写代码的过程中使用注释需要遵循下述多个规则。
规则1:避免杂乱的注释,如使用一排星号或短横杆,而应使用空行隔开。
规则2:注释内容与注释符之间用一个空格隔开。
规则3:不在块注释周围加上边框。
规则4:注释采用统一样式和标点。
规则5:编写代码时就一同添加注释,修改代码时,一并修改相应注释,使其保持最新。
规则6:注释应使用完整句子,同时追求简明而精确地阐明代码,避免多义性,避免多余或不适当的注释。
规则7:在较长的代码段的闭合右花括号后允许使用行尾注释提示闭合的相应起点。
规则8:在字段声明及自动属性后允许使用行尾注释,需在公共制表位处对齐。
规则9:对由循环或逻辑分支组成的代码使用注释。
规则10:对每个方法使用文档注释。
规则11:如果注释难以准确阐明代码,或是十分复杂,则应考虑重构成更易用的代码,在维护性和性能之间做出平衡。
规则12:部署前,移除临时或无关的注释。
2 单行注释
单行注释使用双左斜杆进行注释,留一空格后接注释内容。
单行注释主要用于字段或属性声明,以及代码或代码段的注释,抑或是用于右括号后的注释。
示例:
// 这是一个属性
private int Number { get; set; } private int anotherNumber; // 这是一个字段 if (true)
{
//
// Do Something here
//
} // true
3 多行注释
传统的多行注释指的是使用"/*"开头和用"*/"这种块注释符,在程序开发中不使用块注释符,而应使用多个单行注释。使用IDE的快捷键便能迅速在被选中内容的每一行开头加上单行注释符以实现多行注释。以Visual Studio 2013为例,同时按下Ctrl+K后,松开K键,保持Ctrl键按下,再按下C键即可完成对选定内容进行注释,取消注释只需把最后的C键改按为U键。
4 文档注释
理论上,在声明类、接口、方法、属性及字段均应使用文档注释,但为了方便,允许属性和字段不使用此类注释,而改用单行注释。注意,文档注释不能用于命名空间,原因是一个命名空间可以跨越几个程序集,而且不必同时加载所有程序集。
文档注释采用XML标签进行标记,必须遵守符合格式标准的XML规则,在代码完成后能够直接生成帮助文档。对于非普通桌面应用软件开发,不需要为用户提供帮助文档的话,不必学会所有的用法,只需在团队开发中遵循统一的样式,使代码功能易于理解即可。下面通过示例介绍在类和方法的声明处使用简单的文档注释。
示例:
/// <summary>
/// 类型:类
/// 名称:Sample
/// 作者:taixihuase
/// 作用:用于示例
/// 编写日期:2015/03/28
/// </summary>
class Sample
{
// 存储类名称的属性
public string ClassName { get; set; } /// <summary>
/// 类型:方法
/// 名称:SetClassName
/// 作者:taixihuase
/// 作用:设置类名的值
/// 编写日期:2015/03/28
/// </summary>
/// <param name="className">作为类名称的字符串</param>
/// <returns>void</returns>
void SetClassName(string className)
{
ClassName = className;
} /// <summary>
/// 类型:方法
/// 名称:ShowSample
/// 作者:taixihuase
/// 作用:演示示例
/// 编写日期:2015/03/28
/// <param>无</param>
/// <returns>void</returns>
/// </summary>
void ShowSample()
{
// 单行显示类名称
Console.WriteLine(ClassName);
}
}
文档注释采用三个左斜杆标识,<summary>标签为必须部分,其包括的内容用来描述类、方法等的层次、名称、作者、作用和编写日期等等信息。
类的注释中可以仅包含<summary>块,而方法的注释应当包含表示参数的<param>标签以及表示返回值的<returns>标签。
在XML中元素中可以包含属性,示例中的<param name="className">添加了一个name属性,并设置为"className",表示该方法有一个带一个形参名为"className"的版本。
<returns>标签表示方法的返回值,如果方法无返回值,则填写"void",如果是构造函数或者析构函数(极少使用),则填写“无”。
下面列出了更多的文档注释标签的说明。
标签 | 用法 | 作用 |
<c> |
<c>text</c> |
<c> 标记为您提供了一种将说明中的文本标记为代码的方法。 使用<code>将多行指示为代码。 |
<code> |
<code>content</code> |
<code>标记为您提供了一种将多行指示为代码的方法。 使用<c>指示应将说明中的文本标记为代码。 |
<example> |
<example>description</example> |
使用<example>标记可以指定使用方法或其他库成员的示例。 这通常涉及使用<code>标记。 |
<exception>* |
<exception cref="member">description</exception> cref = "member": |
<exception>标记使您可以指定哪些异常可被引发。 此标记可用在方法、属性、事件和索引器的定义中。 |
<include>* |
<include file='filename' path='tagpath[@name="id"]' /> filename:包含文档的文件名。该文件名可用路径加以限定。 |
<include>标记使您得以引用描述源代码中类型和成员的另一文件中的注释。 这是除了将文档注释直接置于源代码文件中之外的另一种可选方法。 |
<list> |
<list type="bullet" | "number" | "table"> |
<listheader>块用于定义表或定义列表中的标题行。 定义表时,只需为标题中的项提供一个项。 列表中的每一项都用一个<item>块来描述。 创建定义列表时,既需要指定 term 也需要指定 description。 但是,对于表、项目符号列表或编号列表,只需为 description 提供一个项。 列表或表所拥有的<item>块数可以根据需要而定。 |
<para> |
<para>content</para> |
<para>标记用于诸如<summary>、<remarks>或<returns>等标记内,使您得以将结构添加到文本中。 |
<param>* |
<param name='name'>description</param> description:参数说明。 |
<param>标记应当用于方法声明的注释中,以描述方法的一个参数。 有关 <param> 标记的文本将显示在 IntelliSense、对象浏览器和代码注释 Web 报表中。 |
<paramref> |
<paramref name="name"/> |
<paramref>标记提供了指示代码注释中的某个单词(例如在<summary>或<remarks>块中)引用某个参数的方式。 可以处理 XML 文件来以不同的方式格式化此单词,比如将其设置为粗体或斜体。 |
<permission>* |
<permission cref="member">description</permission> |
<permission>标记使您得以将成员的访问记入文档。 使用 PermissionSet 类可以指定对成员的访问。 |
<remarks> |
<remarks>description</remarks> |
<remarks>标记用于添加有关某个类型的信息,从而补充由<summary>所指定的信息。 此信息显示在对象浏览器中。 |
<returns> |
<returns>description</returns> |
<returns>标记应当用于方法声明的注释,以描述返回值。 |
<see>* |
<see cref="member"/> |
<see>标记使您得以从文本内指定链接。 使用<seealso>指示文本应该放在“另请参见”节中。 |
<seealso>* |
<seealso cref="member"/> |
<seealso>标记使您得以指定希望在“请参见”一节中出现的文本。 使用<see>从文本内指定链接。 |
<summary> |
<summary>description</summary> |
<summary>标记应当用于描述类型或类型成员。 使用<remarks>添加针对某个类型说明的补充信息。 <summary>标记的文本是唯一有关 IntelliSense 中的类型的信息源,它也显示在对象浏览器中。 |
<typeparam>* |
<typeparam name="name">description</typeparam> |
在泛型类型或方法声明的注释中应该使用<typeparam>标记描述类型参数。 为泛型类型或方法的每个类型参数添加标记。 <typeparam>标记的文本将显示在对象浏览器代码注释 Web 报表 IntelliSense 中。 |
<typeparamref> |
<typeparamref name="name"/> |
使用此标记,文档文件的使用者能够以某种独特的方式设置单词的格式,例如以斜体显示。 |
<value> |
<value>property-description</value> |
<value>标记使您得以描述属性所代表的值。 请注意,当在 Visual Studio .NET 开发环境中通过代码向导添加属性时,它将会为新属性添加<summary>标记。 然后,应该手动添加<value>标记以描述该属性所表示的值。 |
四、声明规则
1 行声明数
一行只做一个声明。
示例:
private int number; // 推荐 private int number1, number2; // 不推荐
2 初始化
建议在变量声明时进行初始化。
3 位置
变量声明位于块的开始处,循环计数变量例外。
避免因不同层次的变量重名导致外层变量被屏蔽。
4 字段声明
只使用私有实例字段,为字段提供属性访问器。
示例:
private int number;
public int Number
{
get { return number; }
} public int AnotherNumber { get; private set; } // 同样实现只读的自动属性
五、命名规范
1 命名概述
C#在对名称进行命名的时候,与其他语言例如C++有所不同,应当遵循以下规则。
规则1:名称应准确详尽说明是“什么”而非“如何”。
规则2:避免在名称中公开实现基础。如采用浮点型,在C++编写的程序中有些程序员采用匈牙利命名法,会用"m_f"等作为命名前缀以告知开发者是一个浮点型类成员变量。同时,除变量之外,例如方法的内部实现也不应体现在名称上,如“GetNextArrayElement”则是不合法的名称。
规则3:避免难以解释的、易被主观误解而产生多义性的名称。
规则4:布尔型变量包含"is"。
规则5:使用互补对,如"min"与"max"等。
规则6:对生命周期极短的变量仍使用有意义的名称,仅允许循环计数变量采用 i、j 等名称。
规则7:不使用原义字符串或原义数字,而使用常量,如使用"WINDOW_HEIGHT"而非"1440",这样不仅易于维护与理解,还能减少硬编码。
规则8:只使用众所周知的不会引起多义性的缩写,如"html"。
规则9:方法应该以动词命名,其余以名词性单词或短语命名。
规则10:避免使用与常用的 .NET 框架命名空间重复的类名称。避免使用和关键字冲突的标识符。
2 大小写规则
C#对不同类型的标识符进行命名的大小写规则如下表。
标识符 | 大小写 | 示例 |
类 | Pascal | Application |
枚举类型 | Pascal |
ExceptionTypes 不包含 Enum 后缀 |
枚举值 | Pascal | * |
委托 | Pascal |
MouseMoveEventHandler 若不采用观察者模式,则不需要以 EventHandler 后缀修饰 |
事件 | Pascal | MouseMove |
异常类 | Pascal |
ThreadException 总是以 Exception 后缀结尾 |
只读的静态字段 | Pascal | ClassName |
接口 | Pascal |
IDisposable 总是以 I 前缀开始 |
方法 | Pascal | ToString |
命名空间 | Pascal | System.Collections |
属性 | Pascal | FirstName |
公共实例字段 | Pascal |
FirstName 很少使用,属性优于使用公共实例字段 |
受保护的实例字段 | Camel |
firstName 很少使用,属性优于使用受保护的实例字段 |
私有实例字段 | Camel | firstName |
参数 | Camel | filePath |
方法内的变量 | Camel | firstName |
常量 | 全部大写 |
PROJECT_FILE_PATH 沿袭C++的命名规则,单词之间以下划线隔开 |
两个字母内 | 全部大写 |
UI 三个字母及以上(包括缩写)不采用全部大写 |
3 缩略词规则
首字母缩略词由术语或短语中已成形的单词字母所组成。例如,HTML 就是 Hypertext Markup Language 的首字母缩略词。首字母缩略词区别于缩写词,因为缩写词是被缩短的一个单独的单词,如ID。
含字母缩略词的名称应该遵循三点规则。
规则1:除了基于骆驼命名法的标识符的第一个单词之外,应该同时大写两个字符的字母缩略词。
示例:
public int DBRate { get; set; } // DB为两个字符缩略词,属性采用帕斯卡命名法,故DB大写 private int ioChannel; // io为两个字符缩略词,私有字段采用骆驼命名法,故io小写
规则2:除了基于骆驼命名法的标识符的第一个单词以外,只对三个以上字符的字母缩略词的第一个字符进行大写。
示例:
// Xml为三个字符缩略词,类采用帕斯卡命名法,故Xml首字母大写
class XmlWriter
{
//
// Something here
//
// 同样是xml,参数采用骆驼命名法,故xml首字母小写
public void DisplayContent(XmlReader xmlReader);
//
// Other functions here
//
}
规则3:基于骆驼命名法的标识符的开始部分,不要对任何字母缩略词的任何字符进行大写。规则1与规则2均遵循了这一点。
4 命名空间
命名空间一般采用公司或团队名+项目名(后面可以跟上功能或设计名称)的格式组成。注意,命名空间不能与类名称相同。对每个文件都使用命名空间包装,这样利于进行文件间的逻辑组合,并防止与外部类库冲突。
示例:
namespace Blizzard.WorldOfWarcraft
{
// Something here
}
5 类
类在命名时应采用名词或名词性短语,不使用任何前缀及下划线。采用名词全称,除非缩略词总所周知。在继承体系中,派生类大多数情况采用复合名词,第二部分应是基类名词,但也有例外,比如Sharp类与Rectangle类。
6 接口
接口统一用 I 前缀,使用名词或描述行为的形容词命名。其余注意点与类的命名相似。当一个类是接口的标准执行时,则类与接口应用相似的命名,其不同之处只在于前缀 I 的有无。
示例:
public interface IComponent
{
// Something here
} public class Component: IComponent
{
// Something here
}
7 方法
方法应使用动词或动词短语命名,且不应在名称中暴露其底层实现基础。
8 属性
编码一开始时就为字段提供属性。属性对于性能的损失是微乎其微的,而它却如同两个方法,可以做更灵活的设计,包括数据管理、数据检测、线程同步等。接口内不能定义数据成员,但可以使用属性。索引器也是属性的一种。同时,尽可能使用静态属性代替公共静态字段。
示例:
private string name;
public string Name
{
get
{
if (name == null)
{
return "No Name";
}
else
{
return name;
}
} set
{
lock(this)
{
if (string.IsNullOrEmpty(value))
{
throw new ArgumentException('Name cannot be blank', 'Name');
}
else
{
name = value;
}
}
}
}
9 字段
字段不使用匈牙利命名法,不使用前缀区分是否静态字段。仅公有字段采用帕斯卡命名法,受保护的或私有字段采用骆驼命名法。
10 集合
集合如哈希表、查询、堆栈、字典和列表等,命名建议使用复数。
11 枚举
只与某个特定类相关联的枚举在该类内定义,否则,枚举与类同级。枚举一般使用名词或名词组合,简单枚举使用单数,标志枚举使用复数。大多数情况下不需要更改枚举的默认类型,除非枚举是标志枚举,且标志多于32个(此时 int 类型装不下)或者枚举被非常大量且频繁地使用。
示例:
[Flags]
public enum Human : byte
{
Male = 0x01,
Female = 0x10
}
12 事件
事件采用动词命名,不添加任何前缀。使用其现在分词及过去分词形式分别表示事件前与事件后。对于事件处理程序,使用"EventHandler"后缀,并指定类型为"Object"的参数"sender"以及特定事件类类型的参数"e"。事件类均继承于"EventArgs"类。
示例:
public class ValueEventArgs : EventArgs
{
private int value;
public int Value
{
get { return this.value; }
set { this.value = value; }
} public ValueEventArgs(int value)
{
this.value = value;
}
} public delegate void ValueChangeEventHandler(object sender, ValueEventArgs e);
六、语句
1 行语句数
一行最多只包含一条语句。
2 复合语句
复合语句的子语句需要缩进,遵循括号与空格规则。且仅有一条子语句时也不用省略花括号,这样做层次更清晰,且利于扩展。
3 if-else结构
凡是使用了 if 语句的地方,如果存在另一逻辑相关的分支,均使用 else 语句或者 else if 语句嵌套,不要省略。
示例:
enum Status
{
Below = ,
Equal = ,
Greater = ;
} // Something here public Status Compare(int x, int y)
{
if (x < y)
{
return Status.Below;
}
else if (x > y)
{
return Status.Greater;
}
else
{
return Status.Equal;
}
}
4 循环语句
for 语句中需要在其圆括号内完整填写初始化语句、条件判断语句及更新语句。
foreach 语句中,如果可以得知集合元素的类型,则一般应使用该类型,如果不能明确获知类型或类型名称过长时,允许使用 var 关键字。注意,foreach 语句在带来方便的同时也会降低性能,考虑通过获取集合元素数目后使用 for 循环代替。
如果有必要使用空循环体,应明确做出注释。
不要在循环过程中修改循环计时器,除非这样做有特殊用途。
5 switch-case结构
如果 if-else-else if 结构有过多分支,则应考虑使用多分支语句 switch-case。
在 switch-case 结构中,每个 case 独占一行,且按字母排序。每个非空分支都要以 break 语句结束,且两个分支之间需用一个空行隔开。应当为所有switch语句提供 default 分支,无论该分支是否有进行任何工作。
示例:
switch (behaviorType)
{
case Bite :
//
// statements
//
break; case Jump :
//
// statements
//
break; default :
break;
}
6 break、continue语句
在使用 break 或 continue 语句的地方,如果不是处于代码块的末尾,则应该在该语句后面空出一行。
7 try-catch-finally结构
在C#中使用异常处理机制所带来的性能损失比C++要小得多,因此应在有可能发生错误的代码块周围使用 try 块,在 catch 块处理异常,在 finally 块进行清理工作等。
若能够预估出 try 块中的代码可能会发生的某种特定类型的异常,可以使用 throw 关键字手工抛出该类型的异常。
catch 块必须紧跟在 try 块之后。catch 块一般需带一个名为"e"、类型为某个异常类或是异常基类的参数。catch 块可以同时使用多个,但要注意它们的先后顺序,带有子类异常类参数的异常处理块必须放在带有其父类类型参数的块之前。 建议在最后总是添加一个参数类型为"Exception"的 catch 块来进行默认的异常处理。这样,当找不到对应类型的处理程序时,最终会被这个块所捕捉。
finally 块既能与 try 关键字共同使用,也可跟在 try-catch 结构之后。finally 块是可选的,但建议无论是否出现异常,都使用 finally 块来做后续清理工作,这是一种比等待垃圾回收更好的选择。
try-catch-finally结构可以嵌套使用,但一般不能嵌套太多层,否则将会出现层次混乱。
不要在 catch 块或 finally 块中使用 break 或 continue 语句,否则会导致错误。
catch 块和 finally 块中的代码应该非常短,而且成功率极高,避免自己又抛出一个异常。
下表是常见的异常类,可以用来指定参数类型或手工抛出异常,也可使用派生于 Exception 类的自定义异常类。
异常类名称 | 简单描述 |
ArgumentException | 参数错误:方法的参数无效 |
ArgumentNullException | 参数为空:给方法传递一个不可接受的空参数 |
ArithmeticException | 数学计算错误:由于数学运算导致的异常,覆盖面广 |
ArrayTypeMismatchException | 数组类型不匹配 |
DivideByZeroException | 被零除 |
FormatException | 参数的格式不正确 |
IndexOutOfRangeException | 索引超出范围,小于0或比最后一个元素的索引还大 |
InvalidCastException | 非法强制转换,在显式转换失败时引发 |
MemberAccessException | 访问错误:类型成员不能被访问 |
MulticastNotSupportedException | 不支持的组播:组合两个非空委派失败时引发 |
NotSupportedException | 调用的方法在类中没有实现 |
NullReferenceException | 引用空引用对象时引发 |
OutOfMemoryException | 无法为新语句分配内存时引发,内存不足 |
OverflowException | 溢出 |
*Exception | 栈溢出 |
TypeInitializationException | 错误的初始化类型:静态构造函数有问题时引发 |
NotFiniteNumberException | 无限大的值:数字不合法 |
示例:
int lhs = ;
int rhs = ;
int result; try
{
try
{
result = lhs / rhs;
Console.WriteLine("{0}", result);
}
catch (MemberAccessException e)
{
Console.WriteLine(e.Message);
}
finally
{
Console.WriteLine("finally1");
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
finally
{
Console.WriteLine("finally2");
}
8 lambda表达式
在任何可以使用匿名方法的地方采用 lambda 表达式取代。
lambda 表达式 => 操作符的左侧指定参数,当且仅当只有一个参数时圆括号才是可选的,其参数有自动类型推断功能。建议使用IDE的智能提醒完成。
lambda 表达式 => 操作符的右侧指定实现内容,当有多条语句时,应当用花括号括起,可以有任意数量的语句,但一般不超过三条。
为了使代码清晰,操作符右侧实现代码的格式有两种建议:当语句内容简短时可以在一行内完成 lambda 表达式;当语句内容较长或语句较多时,与定义普通方法一样,一行一条语句,左花括号与表达式所在位置同行,而右花括号独立成行,右花括号后需要加分号。
注意,lambda 表达式较适用于取代匿名方法,当某个方法需要多次被调用时,不宜再采用 lambda 表达式就地定义。
示例:
delegate int Sqr(int sqrt);
//
// statements
//
int sqrt = ;
Sqr mySqr = i => { return i * i; }; // 若改为下面这条语句,不会改变 sqrt 的值,原因是委托的参数不是引用类型
// Sqr mySqr = i => i = i * i; // 下面这条语句在两种情况都输出 100
Console.WriteLine(sqrt); // 下面这条语句在两种情况都输出 10000
Console.WriteLine(mySqr(sqrt));