name: format-style description: 工程代码格式化风格指引
Pixeval 格式化风格
若有未详尽覆盖的情况,可参考任意 src 下的代码,但不包括 src/lib 里的第三方或子模块代码
基本要求
- 保留现有文件头。除非所在目录本身就有同类头部,否则不要给原本没有头部的文件新增头部。
- 视图/控件的
.axaml和.axaml.cs文件要成对维护。 - 默认按现代 C# 假设:主项目目标框架是
net10.0,启用了 nullable,LangVersion是latest,并且默认启用编译绑定。
C# 偏好
- 基础代码风格遵循
.editorconfig中的设置。 - 使用 UTF-8、CRLF、末尾换行、4 空格缩进,不使用 tab。
using指令不要用空行拆分导入组。- 使用 file-scoped namespace。
- 尽量使用
var,包括内置类型和一眼可见的类型。 - 使用
nullable,并在所有地方保证空安全,不要留下null警告。对于临时为null的瞬态,可以用null!。 - 优先使用模式匹配:
- 对常量优先使用
is模式匹配而不是==或!=:count is 0、count is not 0、value is null、value is not null、value is not A and not B。 - 用
obj is { } value绑定非空值。 - 使用属性/列表模式,例如
entry is { Width: > 0, Height: > 0 }。 - 对形状不匹配的情况,用
is not { ... }做守卫。
- 对常量优先使用
- 分支语句优先级:
switch表达式 >switch语句 >if/else if链,尤其适合枚举、类 union 的状态、命令和类型分发。 - 当成员能在一行内清楚表达时,优先使用表达式主体:属性、访问器、简单方法、局部函数、转换器和运算符都适用。
- 优先使用对象/集合初始化器、集合表达式(
[]、[.. items])、空条件运算、??、throw表达式、范围/索引运算符、元组命名,以及清晰时的匿名成员推断。 if-else中若主体是一行以内,则可以省略大括号;若分支中有跳转语句(如return、break、continue等)则优先写在if中,并且省略else来减少嵌套层级。- 强制转换后面保留一个空格:
(Type) value。 - 二元运算符两侧保持空格,换行时把运算符放在下一行开头。
- 简单场景优先使用
using var或await using var,不要嵌套多层using块。 using var或await using var若有初始化列表则拆开,避免初始化时发生异常导致资源未正确释放。例如:using var a = new A { X = 1 };应该改为using var a = new A(); a.X = 1;。- 当有意忽略一个非
void返回值时,使用弃元(_ = ...;),表达式主体的方法除外。 - 对“理论上穷尽”的
switch,使用ArgumentOutOfRangeException(nameof(value))或等价的默认分支。 - 如果所有构造函数都调用某一个构造函数,或只有一个构造函数,优先把这个构造函数改为主构造函数。
- 对于返回
Task或Task<T>的方法,若只有返回时用到Task,则不需要将方法声明为异步(async)方法,而是直接返回Task,但方法名仍然需要Async后缀。 - 在非 UI 层的异步方法中,最后一个参数使用
CancellationToken token = default,并在其中所有await的Task使用ConfigureAwait(false)避免切换线程上下文。但在 UI 代码中,不要这样做以避免死锁。 - 对于变量名引用,使用
nameof(...)而不是字符串字面量,包括字符串内插中。 - 对可复用的查找/转换状态,使用
Lazy<T>、FrozenDictionary、生成正则,或者静态只读转换器实例,不要每次调用都重建数据。 - 对
IDisposable,若需要则调用GC.SuppressFinalize(this)。 - 如果可以,则将字段/属性/结构体/方法/局部方法设为
static/readonly/const/getonly/initonly等以增强约束。 - 不要使用旧的扩展方法,而使用 C# 14 中新的 extension blocks,并将同一类型的扩展聚在一起。
- 尽量使用
stackalloc、Span<T>、Memory<T>等来增强性能。 - 抛出异常时提供要意义的消息、参数名称。
- 编写 XML 注释时:
- 对于继承自接口或基类的成员,优先使用
<inheritdoc />来继承文档。 - 对于调用其他方法所以文档可以直接复用的成员,优先使用
<inheritdoc cref="..." />来复用文档。 - 对于引用的类型、成员,使用
<see cref="..." />,并且不要使用全限定名,而是也用using引入。 - 对于成员引用或泛型参数引用,
<paramref name="..." />、<typeparamref name="..." />来提供链接。 - 对于其中的关键字,使用
<see langword="..." />来增强高亮。
- 对于继承自接口或基类的成员,优先使用
- 对于大段多换行的字符串,使用生字符串(
""")来增强可读性,而且前引号之前必须换行。 - 接口中的泛型若可以用协变/逆变,则使用。
- 对于返回值,若无必要(如迭代器方法则除外),则优先使用
IReadOnlyCollection<T>或更严格的IReadOnlyList<T>而不是IEnumerable<T>,以表达集合内元素有限。 - 优先使用
IReadOnlyList<T>或ReadOnlySpan<T>而不是T[]来表示数组,以避免潜在的数组协变。 - 若一个类内容太多,可以拆成形如
.Commands.cs的 partial 文件。 - 对于每个类都较长的多个类,或者关系不大的多个类(例如某个类不是只在另一个类中使用),则拆分成每个类一个文件而不是放在同一个文件里。
- 使用 Linq 时,优先使用方法链式调用而不是查询表达式。
- 命名时:
- 对非抛出式尝试使用
Try*命名(例如TryReset、TryDelete)。 - 类型、属性、事件、方法、可见字段和常量使用 PascalCase。
- 接口以
I开头。 - 私有实例字段使用
_camelCase;私有静态字段和static readonly字段使用_PascalCase。
- 对非抛出式尝试使用
Avalonia XAML
基础代码风格遵循
Settings.XamlStyler中的设置。尽量使用有类型信息的 XAML:
x:DataType="{x:Type vm:SomeViewModel}"TargetType="{x:Type Button}"x:Key="{x:Type controls:SomeControl}"BasedOn="{StaticResource {x:Type ListBox}}"
对于 XAML 内的命名空间引用,使用
xmlns:prefix="using:..."而不是xmlns:prefix="clr-namespace:...;assembly=..."。对于根元素类所在的命名空间,使用local作为命名空间xmlns:local="using:..."。空元素使用自闭合标签。
稳定的样式/模板优先用
StaticResource,和主题相关的画刷/颜色优先用DynamicResource。优先使用编译/有类型绑定配合
x:DataType。对控件主题/模板,使用
ControlTheme、ControlTemplate、诸如PART_ContentPresenter的命名部件,以及TemplateBinding处理模板属性。优先使用标记扩展以减少代码行数,例如使用
{markup:SymbolIcon ...}而不是<fluent:SymbolIcon Symbol="..." />文本布局优先使用
TextTrimming、MaxLines、TextWrapping和资源文本主题,而不是自己写测量逻辑。标准可绑定控件状态使用 Avalonia
StyledProperty,非样式属性或用户控件中定义的需要绑定的属性使用DirectProperty和SetAndRaise。绑定风格:
- 优先使用 Avalonia 语法,如
$parent[...]、#Name等。 - 优先使用 Avalonia 语法而不是转换器,例如
IsVisible="{Binding !!Items.Count}"、{Binding !IsFollowed}。
- 优先使用 Avalonia 语法,如
简单示例
<ControlTheme x:Key="{x:Type controls:AvatarImage}" TargetType="{x:Type controls:AvatarImage}">
<Setter Property="Template">
<ControlTemplate TargetType="{x:Type controls:AvatarImage}">
<Image Source="{TemplateBinding Source}" />
</ControlTemplate>
</Setter>
</ControlTheme>