dotnet/runtime C 编码风格规范:20 条规则详解与 .editorconfig 自动执行机制
发布时间:2026/9/16 21:03:18 锦皓数字建站

dotnet/runtime C# 编码风格规范20 条规则详解与 .editorconfig 自动执行机制【免费下载链接】runtime.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.项目地址: https://gitcode.com/GitHub_Trending/runtime6/runtime本篇技术文章以 dotnet/runtime 仓库官方的 C# 编码风格指南 为主体完整覆盖其 20 条命名、括号、类型引用与语句格式规则并结合仓库根目录 .editorconfig 配置与 eng/formatting/format.sh 自动化脚本展示每条规则在仓库中如何通过 Roslyn/EditorConfig 体系被机器可执行地落地。读完本文后你能够按 runtime 仓库的标准写出合规的 C# 代码并借助dotnet format与 Git pre-commit 钩子让格式检查变成自动化流程。总体原则以 Visual Studio 默认风格为基准runtime 仓库 C# 编码风格的第一条总则是use Visual Studio defaults遵循 Visual Studio 默认设置。在此总则之下官方指南给出了 20 条具体规则。指南同时强调两条执行层面的机制仓库根目录提供了.editorconfig文件.editorconfig使支持 EditorConfig 的 IDE 能够按照上述规则自动格式化 C# 代码仓库使用dotnet format工具dotnet SDK 内置的 dotnet-format 工具确保代码库风格随时间推移保持一致该工具会自动修复代码使其符合指南。指南中第 9 条规则本身也是一条元规则如果某个文件的既有风格与这些指南不一致例如私有成员命名为m_member而非_member该文件的既有风格优先。这意味着在修改历史代码时应先观察文件局部惯例而非机械套用全局规则。括号、缩进与空白控制规则 1、2、7、8、18规则 1Allman 风格括号runtime 使用 Allman 风格括号每个左大括号单独起一行。单行语句块可以省略大括号但该块必须独占一行并正确缩进且不能嵌套在已使用大括号的其他语句块中。唯一的例外是using语句允许嵌套在另一个using语句内——嵌套using从下一行开始、保持相同缩进级别即可即使内层using包含一个受控代码块。规则 24 空格缩进禁用 Tab统一使用 4 个空格缩进禁止使用 Tab 字符。这在 .editorconfig 中以indent_style space和indent_size 4全局生效作用于所有文件。规则 7 与 8空行与游离空格任意位置最多只允许一个空行例如类型成员之间不允许出现连续两个空行避免出现游离的空格spurious free spaces例如if (someVar 0)中括号与内容之间多余的空格。指南建议在使用 Visual Studio 时可开启显示空白View White Space快捷键 CtrlR, CtrlW辅助发现这类问题。规则 18单语句 if 的括号约定这是指南中最细致的一条格式规则具体分三层永远不使用单行形式例如不允许if (source null) throw new ArgumentNullException(source);使用大括号始终被接受且以下情况必须使用if/else if/.../else复合语句中任何一块使用了大括号或某个语句体跨越多行省略大括号仅限于与if/else if/.../else复合语句关联的所有块的体都放在单行上时。该规则在 .editorconfig 中有对应的机器可读配置csharp_prefer_braces true:silent表示建议总是使用大括号静默提示级别csharp_preserve_single_line_statements false:none表示格式化时不保留单行语句形态。命名规范规则 3、12、13、15、20规则 3字段前缀与 readonly 约定字段命名是 runtime 风格中最具辨识度的一点字段类别前缀大小写示例实例私有/内部字段_camelCaseprivate int _count;静态私有/内部字段s_camelCaseprivate static readonly ... s_loadedInDefaultContext线程静态字段t_camelCaseprivate static ThreadLocalT t_cache;公共字段无谨慎使用PascalCase—其他要点能用readonly就用readonly作用于静态字段时readonly必须写在static之后即static readonly而非readonly static公共字段应谨慎使用使用 PascalCase 且不加前缀。仓库源码可以印证这一约定例如 ComActivator.cs 中private static readonly Dictionarystring, AssemblyLoadContext s_assemblyLoadContexts new Dictionarystring, AssemblyLoadContext(StringComparer.InvariantCultureIgnoreCase); ... private static readonly HashSetstring s_loadedInDefaultContext new HashSetstring(StringComparer.InvariantCultureIgnoreCase);.editorconfig 用三条命名规则dotnet_naming_rule把前缀约定变成了可执行检查静态字段要求s_前缀 camelCaseL66-L74private/internal 实例字段要求_前缀 camelCaseL76-L83dotnet_style_readonly_field true:suggestionL94则建议将只读字段声明为readonly。规则 12 与 13常量与方法命名所有常量局部变量与字段使用PascalCase。唯一例外是互操作interop代码常量值应与所调用的目标代码的名称和值完全匹配此时可以保留目标侧的命名所有方法名使用 PascalCase局部函数同样适用。对应地.editorconfig 定义了constant_fields_should_be_pascal_case命名规则对const修饰的字段要求 pascal_case 风格。规则 15字段声明位置字段应声明在类型声明的顶部先于构造方法与其他成员。规则 20主构造函数的参数命名主构造函数primary constructor的参数应像普通参数一样命名使用 camelCase 且不加_前缀。例如// 正确 public ObservableLinkedList(IEnumerableT items) { ... } // 错误不应加下划线前缀 public ObservableLinkedList(IEnumerableT _items) { ... }但如果类型本身不够小、参数使用位置不易一眼看清则应把主构造函数参数赋值给带_前缀的字段例如private readonly IEnumerableT _items items;。类型引用与 var 的使用规则 10、11规则 10var 只在类型右侧显式命名时允许仓库只允许不强制使用var的情形是右侧显式写出了类型通常因为new或显式类型转换。例如// 允许 var stream new FileStream(...); // 不允许右侧类型不可见 var stream OpenStandardInput();指南补充了 target-typednew()的对应约定只能用于左侧显式写出类型的变量定义或字段定义语句中例如FileStream stream new(...);可以而stream new(...);类型在更早的行声明过不可以。.editorconfig 用三条设置表达这一规则的可机器检查近似csharp_style_var_for_built_in_types false:suggestion csharp_style_var_when_type_is_apparent false:none csharp_style_var_elsewhere false:suggestion规则 11使用语言关键字而非 BCL 类型类型引用与方法调用都使用语言关键字而非 BCL 类型名用int, string, float而非Int32, String, Single例如写int.Parse而非Int32.Parse。对应 .editorconfigdotnet_style_predefined_type_for_locals_parameters_members true:suggestion dotnet_style_predefined_type_for_member_access true:suggestion成员可见性、密封与 this.规则 4、5、19规则 4尽量避免this.除非绝对必要。.editorconfig 对字段、属性、方法、事件四类成员均设置dotnet_style_qualification_for_* false:suggestion即出现this.限定时会给出建议级提示规则 5总是显式写出可见性修饰符即使它是默认值——写private string _foo而非string _foo。可见性应是第一个修饰符例如public abstract而非abstract public。.editorconfig 的csharp_preferred_modifier_order定义了完整的修饰符顺序public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async规则 19除非派生确实需要所有 internal 与 private 类型都应声明为static或sealed。与任何实现细节一样未来若需要派生届时可以再调整。using 指令、nameof 与 Unicode 转义规则 6、14、16、17规则 6using 指令位置与排序命名空间导入应写在文件顶部、namespace声明之外并按字母序排序——唯一的例外是System.*命名空间必须放在所有其他命名空间之上。对应 .editorconfig 的csharp_using_directive_placement outside_namespace:suggestion与dotnet_sort_system_directives_first true规则 14nameof 优先只要可行且相关一律使用nameof(...)替代字符串字面量...如异常参数名、事件名、绑定反射字符串等场景规则 16Unicode 转义源码中需要包含非 ASCII 字符时使用 Unicode 转义序列\uXXXX而非字面字符因为字面非 ASCII 字符偶尔会被某些工具或编辑器弄乱规则 17goto 标签缩进使用goto标签时标签的缩进应比当前缩进少一级。.editorconfig 的csharp_indent_labels one_less_than_current正是这一规则的机器表达。示例文件ObservableLinkedList 示范代码官方指南给出了两个示例文件片段展示上述规则的组合运用。以下是文档中的完整示例带...省略号为风格示范摘录ObservableLinkedList1.cs:using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; using System.Diagnostics; using Microsoft.Win32; namespace System.Collections.Generic { public partial class ObservableLinkedListT : INotifyCollectionChanged, INotifyPropertyChanged { private ObservableLinkedListNodeT _head; private int _count; public ObservableLinkedList(IEnumerableT items) { if (items null) throw new ArgumentNullException(nameof(items)); foreach (T item in items) { AddLast(item); } } public event NotifyCollectionChangedEventHandler CollectionChanged; public int Count { get { return _count; } } public ObservableLinkedListNode AddLast(T value) { var newNode new LinkedListNodeT(this, value); InsertNodeBefore(_head, node); } protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e) { NotifyCollectionChangedEventHandler handler CollectionChanged; if (handler ! null) { handler(this, e); } } private void InsertNodeBefore(LinkedListNodeT node, LinkedListNodeT newNode) { ... } ... } }ObservableLinkedList1.ObservableLinkedListNode.cs:using System; namespace System.Collections.Generics { partial class ObservableLinkedListT { public class ObservableLinkedListNode { private readonly ObservableLinkedListT _parent; private readonly T _value; internal ObservableLinkedListNode(ObservableLinkedListT parent, T value) { Debug.Assert(parent ! null); _parent parent; _value value; } public T Value { get { return _value; } } } ... } }从这段示范中可以看到指南规则的实际组合using指令在命名空间之外且System.*在最前规则 6字段_head、_count、_parent、_value采用_camelCase前缀并放在类型顶部规则 3、15_parent/_value使用readonly规则 3if (items null)单语句体独占一行且因体为单行而省略大括号规则 1、18throw new ArgumentNullException(nameof(items))使用nameof规则 14构造方法参数items不带下划线规则 20 的思想所有方法均为 PascalCase规则 13。规则在 .editorconfig 中的逐条落地.editorconfig 位于仓库根目录root true标记为最顶层 EditorConfig 文件是上述指南的可执行版本。下表汇总关键规则与配置的对应关系指南规则.editorconfig 配置位置规则 1Allman 括号csharp_new_line_before_open_brace all.editorconfig#L26规则 1else/catch/finally 另起一行csharp_new_line_before_else/catch/finally true.editorconfig#L27-L29规则 1/18建议总是使用大括号csharp_prefer_braces true:silent.editorconfig#L88规则 24 空格缩进indent_style space/indent_size 4.editorconfig#L11-L12规则 3readonly 建议dotnet_style_readonly_field true:suggestion.editorconfig#L94规则 3s_静态字段前缀static_fields_should_have_prefix命名规则.editorconfig#L66-L74规则 3_camelCase私有/内部字段camel_case_for_private_internal_fields命名规则.editorconfig#L76-L83规则 4避免this.dotnet_style_qualification_for_* false:suggestion.editorconfig#L45-L49规则 5修饰符顺序csharp_preferred_modifier_order.editorconfig#L43规则 6using 在命名空间外、System 优先csharp_using_directive_placement outside_namespace/dotnet_sort_system_directives_first true.editorconfig#L86-L87规则 10var 使用限制csharp_style_var_*三条设置.editorconfig#L52-L54规则 11语言关键字优先dotnet_style_predefined_type_for_*.editorconfig#L55-L56规则 12常量 PascalCaseconstant_fields_should_be_pascal_case命名规则.editorconfig#L58-L64规则 17标签缩进少一级csharp_indent_labels one_less_than_current.editorconfig#L40需要注意 severity 语义:suggestion表示在 IDE 中以建议呈现:silent表示保留但不主动提示:none表示关闭该诊断。例如规则 10 中csharp_style_var_when_type_is_apparent false:none意味着允许类型明显处使用 var但不提示与指南只允许、不强制的措辞一致。除 C# 外.editorconfig 还规定了仓库其他文件的格式基线C/C*.cpp, *.h, *.incurly_bracket_next_line true与indent_brace_style AllmanL176-L179与 C# 的 Allman 风格保持一致生成代码_AssemblyInfo.cs、.notsupported.cs、AsmOffsets.cs标记generated_code trueL20-L21项目文件.csproj、.slnx、.resx等 XML 类文件使用 2 空格缩进L181-L205JSON/YAML 同样 2 空格L204-L205行尾约定.sh脚本使用 LF.cmd/.bat使用 CRLFL207-L211许可头模板file_header_template指定了 MIT 许可头文本L165-L166dotnet format据此统一文件头。自动执行dotnet format 与 Git pre-commit 钩子指南声明风格后仓库还建立了让风格自动保持的工具链具体做法详见 Code Formatting Tools 指南。dotnet format 工具C#/VB 代码依赖 Roslyn 对 EditorConfig 的内置支持在多数 IDE 中无需额外工具即可自动格式化同时也可以用 dotnet SDK 的dotnet format命令做批处理格式化。dotnet format会读取.editorconfig并自动修复代码使其长期符合上述指南。pre-commit 钩子脚本 format.sheng/formatting/format.sh 是仓库提供的提交前格式化脚本其完整逻辑为#!/bin/sh LC_ALLC # Select files to format NATIVE_FILES$(git diff --cached --name-only --diff-filterACM *.h *.hpp *.c *.cpp *.inl | sed s| |\\ |g) MANAGED_FILES$(git diff --cached --name-only --diff-filterACM *.cs *.vb | sed s| |\\ |g) exec 12 if [ -n $NATIVE_FILES ]; then # Format all selected files echo $NATIVE_FILES | xargs ./artifacts/tools/clang-format -stylefile -i # Add back the modified files to staging echo $NATIVE_FILES | xargs git add fi if [ -n $MANAGED_FILES ]; then # Format all selected files echo $MANAGED_FILES | dotnet format whitespace --include - --folder # Add back the modified files to staging echo $MANAGED_FILES | xargs git add fi exit 0从脚本实现看它只对本次暂存区中新增/修改/改名ACM的文件做格式化分两条路径原生代码.h/.hpp/.c/.cpp/.inl调用./artifacts/tools/clang-format -stylefile -i。-stylefile表示使用仓库内的.clang-format配置文件因此工具版本必须与仓库约定一致——这些工具需先运行 eng/formatting/download-tools.shWindows 上为download-tools.ps1下载到artifacts/tools目录托管代码.cs/.vb调用dotnet format whitespace --include - --folder即只对空白类格式做规范化不改动分析器级样式并把结果重新git add回暂存区。启用方式按 Code Formatting Tools 指南 的说明在本地克隆中创建.git/hooks/pre-commit文件内容为#!/bin/sh ./eng/formatting/format.sh然后确认文件可执行chmod x .git/hooks/pre-commit若git config core.hooksPath指向了别处需要用git config core.hooksPath .git/hooks指回。由于 Git for Windows 附带 Git Bash该脚本在 Windows 与非 Windows 平台均可工作。在 IDE 层面Visual Studio Code 可通过.vscode/settings.json中的editor.formatOnSave: true开启保存即格式化Visual Studio 则可在Tools Options下配置语句结束/块结束时格式化配合上述 Git 钩子即可获得完整的自动化体验。其他语言与脚本的风格约定指南最后一节说明对 C# 之外的语言当前最好的建议就是一致性consistency编辑现有文件时新代码与改动应与该文件内的既有风格保持一致新建文件时应符合所在组件的风格若是一个全新组件任何被广泛接受的合理风格都可以脚本文件可参考 Microsoft 官方脚本博客中的 PowerShell 技巧与最佳实践条目。小结runtime 仓库的 C# 编码风格可以概括为三层20 条显式规则定义命名、括号、类型引用与语句格式遵循 Visual Studio 默认风格为总纲仓库根目录的 .editorconfig把可机器表达的规则转化为命名规则与格式开关让 IDE 与 Roslyn 格式化引擎自动执行dotnet format format.sh pre-commit 钩子则保证即便开发者不手动格式化提交前的代码依然保持仓库统一风格。三者配合使得风格约束从文档约定变成了流水线事实。参考文档与文件编码风格指南原文docs/coding-guidelines/coding-style.md格式化工具指南docs/coding-guidelines/code-formatting-tools.md全局格式配置.editorconfig提交前格式化脚本eng/formatting/format.sh、eng/formatting/download-tools.sh风格示例源码src/coreclr/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComActivator.cs【免费下载链接】runtime.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.项目地址: https://gitcode.com/GitHub_Trending/runtime6/runtime创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。