windows C#-默认约定(上)
编码约定对于在开发团队中维护代码可读性、一致性和协作至关重要。 遵循行业实践和既定准则的代码更易于理解、维护和扩展。 大多数项目通过代码约定强制要求样式一致。 dotnet/docs 和 dotnet/samples 项目并不例外。 在本系列文章中,你将了解我们的编码约定和用于强制实施这些约定的工具。 你可以按原样采用我们的约定,或修改它们以满足团队的需求。
我们对约定的选择基于以下目标:
正确性:我们的示例将会复制并粘贴到你的应用程序中。 我们希望如此,因此我们需要代码具有复原能力且正确无误,即使在多次编辑之后也是如此。
教学:示例的目的是教授 .NET 和 C# 的全部内容。 因此,我们不会对任何语言功能或 API 施加限制。 相反,这些示例会告知某个功能在何时会是良好的选择。
一致性:读者期望我们的内容提供一致的体验。 所有示例应遵循相同的样式。
采用:我们积极更新示例以使用新的语言功能。 这种做法提高了对新功能的认识,并且提高了所有 C# 开发人员对这些功能的熟悉程度。
Microsoft 会使用这些准则来开发示例和文档。 它们摘自 .NET 运行时、C# 编码样式和 C# 编译器 (roslyn) 准则。 我们选择这些准则是因为它们已经经过了多年开放源代码开发的测试。 他们帮助社区成员参与运行时和编译器项目。 它们是常见 C# 约定的示例,而不是权威列表(有关此内容,请参阅框架设计指南)。
教学和采用目标是文档编码约定不同于运行时和编译器约定的原因。 运行时和编译器对热路径具有严格的性能指标。 许多其他应用程序则并非如此。 我们的教学目标要求我们不会禁止任何构造。 相反,示例显示了何时应使用构造。 与大多数生产应用程序相比,我们在更新示例方面更加积极。 我们的采用目标要求我们显示你目前应该编写的代码,即使去年编写的代码无需更改。
工具和分析器
工具可帮助团队强制实施约定。 可以启用代码分析来强制实施你偏好的规则。 还可以创建 editorconfig,以便 Visual Studio 可自动强制实施样式准则。 作为起点,可以复制 dotnet/docs 存储库的文件以使用我们的样式。
借助这些工具,团队可以更轻松地采用首选的准则。 Visual Studio 将在范围中的所有 .editorconfig 文件中应用规则,以设置代码的格式。 可以使用多个配置来强制实施企业范围的约定、团队约定,甚至精细的项目约定。
启用的规则被违反时,代码分析会生成警告和诊断。 可以配置想要应用于项目的规则。 然后,每个 CI 生成会在违反任何规则时通知开发人员。
语言准则
以下部分介绍了 .NET 文档团队在准备代码示例和示例时遵循的做法。 一般情况下,请遵循以下做法:
- 尽可能利用新式语言功能和 C# 版本。
- 避免陈旧或过时的语言构造。
- 仅捕获可以正确处理的异常;避免捕获泛型异常。
- 使用特定的异常类型提供有意义的错误消息。
- 使用 LINQ 查询和方法进行集合操作,以提高代码可读性。
- 将异步编程与异步和等待用于 I/O 绑定操作。
- 请谨慎处理死锁,并在适当时使用 Task.ConfigureAwait。
- 对数据类型而不是运行时类型使用语言关键字。 例如,使用 string 而不是 System.String,或使用 int 而不是 System.Int32。
- 使用 int 而不是无符号类型。 int 的使用在整个 C# 中很常见,并且当你使用 int 时,更易于与其他库交互。 特定于无符号数据类型的文档例外。。
- 仅当读者可以从表达式推断类型时使用 var。 读者可在文档平台上查看我们的示例。 它们没有悬停或显示变量类型的工具提示。
- 以简洁明晰的方式编写代码。
- 避免过于复杂和费解的代码逻辑。
遵循更具体的准则。
字符串数据
使用字符串内插来连接短字符串,如下面的代码所示。
string displayName = $"{nameList[n].LastName}, {nameList[n].FirstName}";
若要在循环中追加字符串,尤其是在使用大量文本时,请使用 System.Text.StringBuilder 对象。
var phrase = "lalalalalalalalalalalalalalalalalalalalalalalalalalalalalala";
var manyPhrases = new StringBuilder();
for (var i = 0; i < 10000; i++)
{
manyPhrases.Append(phrase);
}
//Console.WriteLine("tra" + manyPhrases);
数组
当在声明行上初始化数组时,请使用简洁的语法。 在以下示例中,不能使用 var 替代 string[]
string[] vowels1 = { "a", "e", "i", "o", "u" };
如果使用显式实例化,则可以使用 var。
var vowels2 = new string[] { "a", "e", "i", "o", "u" };
委托
使用 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;
使用 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)}");
如果创建委托类型的实例,请使用简洁的语法。 在类中,定义委托类型和具有匹配签名的方法。
public delegate void Del(string message);
public static void DelMethod(string str)
{
Console.WriteLine("DelMethod argument: {0}", str);
}
创建委托类型的实例,然后调用该实例。 以下声明显示了紧缩的语法。
Del exampleDel2 = DelMethod;
exampleDel2("Hey");
以下声明使用了完整的语法。
Del exampleDel1 = new Del(DelMethod);
exampleDel1("Hey");
try-catch 和 using 语句正在异常处理中
对大多数异常处理使用 try-catch 语句。
static double ComputeDistance(double x1, double y1, double x2, double y2)
{
try
{
return Math.Sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}
catch (System.ArithmeticException ex)
{
Console.WriteLine($"Arithmetic overflow or underflow: {ex}");
throw;
}
}
通过使用 C# using 语句简化你的代码。 如果具有 try-finally 语句(该语句中 finally 块的唯一代码是对 Dispose 方法的调用),请使用 using 语句代替。
在以下示例中,try-finally 语句仅在 finally 块中调用 Dispose。
Font bodyStyle = new Font("Arial", 10.0f);
try
{
byte charset = bodyStyle.GdiCharSet;
}
finally
{
if (bodyStyle != null)
{
((IDisposable)bodyStyle).Dispose();
}
}
可以使用 using 语句执行相同的操作。
using (Font arial = new Font("Arial", 10.0f))
{
byte charset2 = arial.GdiCharSet;
}
使用不需要大括号的新 using 语法:
using Font normalStyle = new Font("Arial", 10.0f);
byte charset3 = normalStyle.GdiCharSet;