一、概述
代码标准对于在开发团队中维护代码可读性、一致性和协作至关重要。 遵循行业实践和既定准则的代码更易于理解、维护和扩展。 大多数项目通过代码约定强制要求样式一致。本文我们将讨论编码约定和用于强制实施这些约定的工具。
二、代码预定的目标
- 正确性
在编辑代码时,需要代码具有复原能力且正确无误,即使在多次编辑之后也是如此。
- 一致性
在编写代码中,我们需要遵循相同的规则,使之在整个项目周期中遵循相同的规则样式。
三、代码约束工具和分析器
工具可帮助团队强制实施标准。可以启用代码分析来强制实施代码规则。可以创建editorconfig,以便Visual Studio可自动强制实施样式标准。
借助这些工具,团队可以更轻松地采用首选的标准。Visual Studio将在范围中的所有.editorconfig文件中应用规则,以设置代码的格式。可以使用多个配置来强制实施企业范围的标准、团队标准甚至精细的项目标准。
启用的规则被违反时,代码分析会生成警告和诊断。可以配置想要应用于项目的规则。然后,每个CI生成会在违反任何规则时通知开发人员。
四、C#语言准则
一般情况C#需要遵从以下准则:
- 尽可能利用新式语言功能和 C# 版本
- 避免陈旧或过时的语言构造
- 仅捕获可以正确处理的异常;避免捕获泛型异常
- 使用特定的异常类型提供有意义的错误消息
- 使用 LINQ 查询和方法进行集合操作,以提高代码可读性
- 将异步编程与异步和等待用于 I/O 绑定操作
- 谨慎处理死锁状态,并在适当时使用Task.ConfigureAwait
- 对数据类型而不是运行时类型使用语言关键字。例如:定义字符串使用string而不是System.String,或使用int而不是System.Int32
- 使用 int 而不是无符号类型。 int 的使用在整个 C# 中很常见,并且当你使用 int 时,更易于与其他库交互。 特定于无符号数据类型的文档例外
- 仅当读者可以从表达式推断类型时使用 var
- 以简洁明晰的方式编写代码
- 避免过于复杂和费解的代码逻辑
五、字符串约定
5.1 使用字符串内插来连接短字符串
string displayName = $"{JsonList[n].LastName}, {JsonList[n].FirstName}";
5.2 插入大文本时,使用System.Text.StringBuilder
对象
var phrase = "lalalalalalalalalalalalalalalalalalalalalalalalalalalalalala"; var manyPhrases = new StringBuilder(); for (var i = 0; i < 10000; i++) { manyPhrases.Append(phrase); } var response = stringBuilder.ToString();
六、数组约定
- 当在声明行上初始化数组时,请使用简洁的语法
string[] vowels1 = { "H", "O", "O", "L", "G" };
- 如果使用显式实例化,则可以使用
var
var word=new String[]{"G","O","Y","E","E","R"};
七、委托约定
7.1 使用Func<>
和Action<>
,而不是定义委托类型
Action<string> actionExample1 = x => Console.WriteLine($"x is: {x}"); Action<string, string> actionExample2 = (x, y) => Console.WriteLine($"x is: {x}, y is {y}"); Func<string, int> funcExample1 = x => Convert.ToInt32(x); Func<int, int, int> funcExample2 = (x, y) => x + y;
7.2 使用Func<>
和Action<>
委托定义的签名来调用方法
actionExample1("string for x"); actionExample2("string for x", "string for y"); Console.WriteLine($"The value is {funcExample1("1")}"); Console.WriteLine($"The sum is {funcExample2(1, 2)}");
7.3 如果创建委托类型的实例,请使用简洁的语法
public delegate void Del(string message); public static void DelMethod(string str) { Console.WriteLine("DelMethod argument: {0}", str); }
7.4 创建委托类型的实例,然后调用该实例
Del exampleDel2 = DelMethod; exampleDel2("Hey");
八、&&
和 ||
运算符
8.1 在执行比较时,使用&&
而不是 &
,使用||
而不是|
Console.Write("Enter a dividend: "); int dividend = Convert.ToInt32(Console.ReadLine()); Console.Write("Enter a divisor: "); int divisor = Convert.ToInt32(Console.ReadLine()); if ((divisor != 0) && (dividend / divisor) is var result) { Console.WriteLine("Quotient: {0}", result); } else { Console.WriteLine("Attempted division by 0 ends up here."); }
九、new运算符
9.1 使用对象实例化的简洁形式
var firstExample = new ExampleClass(); ExampleClass instance2 = new();
前面的声明等效于下面的声明:
ExampleClass secondExample = new ExampleClass();
9.2 使用对象初始值设定项简化对象创建
var thirdExample = new ExampleClass { Name = "Desktop", ID = 37414, Location = "Redmond", Age = 2.3 };
十、静态成员
使用类名调用static
成员:ClassName.StaticMember。通过明确静态访问使代码更易于阅读。请勿使用派生类的名称来限定基类中定义的静态成员。编译代码时,代码可读性具有误导性,如果向派生类添加具有相同名称的静态成员,代码可能会被破坏。
十一、LINQ
查询
11.1 对查询变量使用有意义的名称
var seattleCustomers = from customer in customers where customer.City == "Seattle" select customer.Name;
11.2 使用别名确保匿名类型的属性名称都使用 Pascal 大小写格式正确大写
var localDistributors = from customer in customers join distributor in distributors on customer.City equals distributor.City select new { Customer = customer, Distributor = distributor };
11.3 如果结果中的属性名称模棱两可,请对属性重命名
var localDistributors2 = from customer in customers join distributor in distributors on customer.City equals distributor.City select new { CustomerName = customer.Name, DistributorID = distributor.ID };
11.4 在查询变量和范围变量的声明中使用隐式类型化
var seattleCustomers = from customer in customers where customer.City == "Seattle" select customer.Name;
11.5 对齐from
子句下的查询子句,其他查询子句前面使用where
子句,确保后面的查询子句作用于作用于经过缩减和筛选的一组数据
var seattleCustomers2 = from customer in customers where customer.City == "Seattle" orderby customer.Name select customer;
11.6 使用多行 from
子句代替join
子句来访问内部集合
var scoreQuery = from student in students from score in student.Scores! where score > 90 select new { Last = student.LastName, score };
十二、隐式类型本地变量
12.1 当变量的类型在赋值右侧比较明显时,对局部变量使用隐式类型
var message = "This is clearly a string."; var currentTemperature = 27;
12.2 当类型在赋值右侧不明显时,请勿使用var
int numberOfIterations = Convert.ToInt32(Console.ReadLine()); int currentMaximum = ExampleClass.ResultSoFar();
12.3 不要使用变量名称指定变量的类型
12.4 避免使用var
来替代dynamic
如果想要进行运行时类型推理,请使用 dynamic
12.5 在for循环中对循环变量使用隐式类型
var phrase = "lalalalalalalalalalalalalalalalalalalalalalalalalalalalalala"; var manyPhrases = new StringBuilder(); for (var i = 0; i < 10000; i++) { manyPhrases.Append(phrase); }
12.6 不要使用隐式类型在foreach循环中类型
不要使用隐式类型化来确定 foreach
循环中循环变量的类型。 在大多数情况下,集合中的元素类型并不明显。 不应仅依靠集合的名称来推断其元素的类型。
12.7 对 LINQ 查询中的结果序列使用隐式类型
关于LINQ的部分说明了许多LINQ
查询会导致必须使用隐式类型的匿名类型。其他查询则会产生嵌套泛型类型,其中var
的可读性更高。
十三、注释样式
- 使用单行注释(//)以进行简要说明
- 避免使用多行注释(/* */)来进行较长的解释。 注释不进行本地化处理。 相反,配套文章中提供了较长的解释
- 若要描述方法、类、字段和所有公共成员,请使用XML注释
- 将注释放在单独的行上,而非代码行的末尾
- 以大写字母开始注释文本
- 以句点结束注释文本
- 在注释分隔符 (//) 与注释文本之间插入一个空格