格式化指南
命名规则
命名规则遵循 Microsoft 的 C# 命名指南 。 在 Microsoft 命名指南未作规定的地方(例如私有变量和局部变量),规则取自 CoreFX C# 编码指南 。
规则摘要:
代码
- 类(Class)、方法(Method)、枚举(Enum)、公共字段(Public Field)、公共属性(Public Property)、
命名空间(Namespace)的名称:使用
PascalCase。 - 局部变量(Local Variable)、参数(Parameter)的名称:使用
camelCase。 - 私有(Private)、受保护(Protected)、内部(Internal)以及受保护内部(Protected Internal)
的字段和属性的名称:使用
_camelCase。 - 命名约定不受 const、static、readonly 等修饰符的影响。
- 在大小写方面,“单词”是指不含内部空格的任何内容,包括缩略词。例如,使用
MyRpc而不是。MyRPC - 接口(Interface)名称以
I开头,例如IInterface。
文件
- 文件名和目录名使用
PascalCase,例如MyFile.cs。 - 尽可能使文件名与文件中主类的名称一致,例如
MyClass.cs。 - 通常,每个文件只包含一个核心类。
组织结构
- 修饰符(Modifier)按以下顺序排列:
public protected internal private new abstract virtual override sealed static readonly extern unsafe volatile async。 - 命名空间的
using声明放在最顶部,位于任何命名空间之前。using导入顺序按字母排列,但System导入始终排在最前面。 - 类成员排序:
- 按以下顺序对类成员进行分组:
- 嵌套类(Nested Class)、枚举(Enum)、委托(Delegate)和事件(Event)。
- 静态(Static)、常量(Const)和只读(Readonly)字段。
- 字段(Field)和属性(Property)。
- 构造函数(Constructor)和终结器(Finalizer)。
- 方法(Method)。
- 在每个分组内,元素应按以下顺序排列:
- Public。
- Internal。
- Protected internal。
- Protected。
- Private。
- 尽可能将接口实现放在一起。
- 按以下顺序对类成员进行分组:
空白规则
源自 Google Java 风格。
- 每行最多一条语句。
- 每条语句最多一个赋值。
- 缩进使用 2 个空格,不使用制表符(Tab)。
- 列宽限制:100。
- 左花括号前不换行。
- 右花括号和
else之间不换行。 - 即使花括号是可选的也要使用。
if/for/while等关键字后以及逗号后加空格。- 左圆括号后和右圆括号前不加空格。
- 一元运算符(Unary Operator)与其操作数之间不加空格。其他所有运算符与每个操作数之间加一个空格。
- 换行规则源自 Google C++ 风格指南,并针对与 Microsoft C# 格式化工具的兼容性做了少量修改:
- 通常,续行缩进 4 个空格。
- 带花括号的换行(例如列表初始化器、Lambda 表达式、对象初始化器等)不算作续行。
- 对于函数定义和调用,如果参数无法全部放在一行,应将它们拆分到多行, 每个后续行与第一个参数对齐。如果空间不足,参数可以放在后续行并缩进 4 个空格。下面的代码示例对此进行了说明。
示例
using System; // `using` 放在最顶部,位于
// 命名空间之外。
namespace MyNamespace { // 命名空间使用 PascalCase。
// 命名空间内缩进。
public interface IMyInterface { // 接口以 'I' 开头
public int Calculate(float value, float exp); // 方法使用 PascalCase
// ...逗号后加空格。
}
public enum MyEnum { // 枚举使用 PascalCase。
Yes, // 枚举值使用 PascalCase。
No,
}
public class MyClass { // 类使用 PascalCase。
public int Foo = 0; // 公共成员变量使用
// PascalCase。
public bool NoCounting = false; // 鼓励使用字段初始化器。
private class Results {
public int NumNegativeResults = 0;
public int NumPositiveResults = 0;
}
private Results _results; // 私有成员变量使用
// _camelCase。
public static int NumTimesCalled = 0;
private const int _bar = 100; // const 不影响命名
// 约定。
private int[] _someTable = { // 容器初始化器使用 2 个
2, 3, 4, // 空格缩进。
}
public MyClass() {
_results = new Results {
NumNegativeResults = 1, // 对象初始化器使用 2 个
NumPositiveResults = 1, // 空格缩进。
};
}
public int CalculateValue(int mulNumber) { // 左花括号前不换行。
var resultValue = Foo * mulNumber; // 局部变量使用 camelCase。
NumTimesCalled++;
Foo += _bar;
if (!NoCounting) { // 一元运算符后不加空格,
// 'if' 后加空格。
if (resultValue < 0) { // 即使可选也使用花括号,
// 比较运算符两侧加空格。
_results.NumNegativeResults++;
} else if (resultValue > 0) { // 花括号和 else 之间不换行。
_results.NumPositiveResults++;
}
}
return resultValue;
}
public void ExpressionBodies() {
// 对于简单的 Lambda 表达式,尽量放在一行,不需要括号或花括号。
Func<int, int> increment = x => x + 1;
// 右花括号与包含左花括号的行的第一个字符对齐。
Func<int, int, long> difference1 = (x, y) => {
long diff = (long)x - y;
return diff >= 0 ? diff : -diff;
};
// 如果在续行换行后定义,则整个函数体缩进。
Func<int, int, long> difference2 =
(x, y) => {
long diff = (long)x - y;
return diff >= 0 ? diff : -diff;
};
// 内联 Lambda 参数也遵循这些规则。如果参数组包含 Lambda 表达式,
// 建议在参数组前加一个换行。
CallWithDelegate(
(x, y) => {
long diff = (long)x - y;
return diff >= 0 ? diff : -diff;
});
}
void DoNothing() {} // 空代码块可以简写。
// 如果可能,通过将新行与第一个参数对齐来换行。
void AVeryLongFunctionNameThatCausesLineWrappingProblems(int longArgumentName,
int p1, int p2) {}
// 如果与第一个参数对齐不可行或难以阅读,
// 则将所有参数换行到新行并缩进 4 个空格。
void AnotherLongFunctionNameThatCausesLineWrappingProblems(
int longArgumentName, int longArgumentName2, int longArgumentName3) {}
void CallingLongFunctionName() {
int veryLongArgumentName = 1234;
int shortArg = 1;
// 如果可能,通过将新行与第一个参数对齐来换行。
AnotherLongFunctionNameThatCausesLineWrappingProblems(shortArg, shortArg,
veryLongArgumentName);
// 如果与第一个参数对齐不可行或难以阅读,
// 则将所有参数换行到新行并缩进 4 个空格。
AnotherLongFunctionNameThatCausesLineWrappingProblems(
veryLongArgumentName, veryLongArgumentName, veryLongArgumentName);
}
}
}Last updated on