csharp-refactoring

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

C# Refactoring (behavior-preserving)

C# 重构(行为保持)

A refactor changes structure, never observable behavior. Do the edit with binding-aware tools, then confirm behavior held with a build + the relevant tests. Keep the effort proportional to the change: a one-line local rename does not need the ceremony a public multi-targeted change does.
一次重构改变结构,绝不改变可观察的行为。使用绑定感知工具进行编辑,然后通过构建和相关测试确认行为保持不变。工作量与变更规模相称:一行局部重命名不需要公开多目标更改那样的仪式。

Mandatory gate: classify before validation or editing

强制门禁:在验证或编辑前进行分类

Read only enough repository context to classify the request. Do this before restoring, building, or making an edit. If the entire requested operation changes behavior or cannot preserve the relevant public/source contract, the correct result of this skill is a decisive handoff, not an implementation attempt:
  1. State:
    Not a behavior-preserving refactor: <specific reason>.
  2. State:
    No files changed.
  3. State:
    Next workflow: <workflow>.
    Then stop. Do not add manual implementation steps, alternatives, an offer to proceed without the workflow, or a follow-up question — even when that workflow is unavailable.
Requested as a "refactor"Classification and action
Framework or NuGet version changeUpgrade. Do not edit or validate the upgrade here; hand off to
dotnet-upgrade
.
New capability, flag, endpoint, tier, or behaviorFeature. Do not implement it here; hand off to the repository's feature workflow.
Threshold, rate, output, or bug-result changeBehavior change. Defer it and hand off to the repository's bug-fix or behavior-change workflow; still complete any clearly separable structural operation.
Tighten or loosen a shipped/public nullable annotationSource-contract change. Leave the declaration and API record unchanged; hand off to the repository's API-contract workflow.
The three-line stop response applies only when the entire request is outside behavior-preserving refactoring. For a mixed request, perform only a clearly separable structural operation and explicitly defer the behavior/contract change. Never state
No files changed.
after completing that structural operation, and never modify tests to make an unauthorized behavior change appear preserved.
只读取足够的仓库上下文以对请求进行分类。在恢复、构建或进行编辑之前完成此操作。如果整个请求的操作会改变行为或无法保持相关的公共/源代码契约,此 skill 的正确结果是果断移交,而不是尝试实现:
  1. 声明:
    Not a behavior-preserving refactor: <specific reason>.
    (这不是行为保持的重构:<具体原因>)
  2. 声明:
    No files changed.
    (未更改任何文件)
  3. 声明:
    Next workflow: <workflow>.
    (下一步工作流:<工作流>)然后停止。不要添加手动实现步骤、替代方案、继续执行的提议或后续问题——即使该工作流不可用。
请求为“重构”分类和行动
Framework 或 NuGet 版本更改升级。 不要在此编辑或验证升级;移交给
dotnet-upgrade
。
新能力、标志、端点、层次或行为功能。 不要在此实现;移交给仓库的功能工作流。
阈值、速率、输出或 bug 结果更改行为更改。 推迟并移交给仓库的 bug 修复或行为更改工作流;仍然完成任何清晰可分离的结构操作。
收紧或放宽已发布/公共的可空注解源代码契约更改。 保持声明和 API 记录不变;移交给仓库的 API 契约工作流。
三行停止响应仅适用于整个请求都在行为保持重构之外。对于混合请求,仅执行清晰可分离的结构操作,并明确推迟行为/契约更改。在完成该结构操作后绝不要声明
No files changed.
,也绝不要修改测试以使未经授权的行为更改看起来被保持。

Work only in the current repository

仅在当前仓库中工作

Resolve the repository root first (
git rev-parse --show-toplevel
) and resolve any prompt-provided relative solution/project path inside that root. Search and edit only that workspace. Never use filesystem-wide search or select a similarly named clone, temporary directory, build output, or another worktree because a file also exists there. If the named path is absent from the current repository, stop and report that mismatch instead of guessing another workspace.
首先解析仓库根目录(
git rev-parse --show-toplevel
),并解析提示提供的相对解决方案/项目路径到该根目录内。仅搜索和编辑该工作区。绝不要使用文件系统范围的搜索或选择名称相似的克隆、临时目录、构建输出或其他工作树,因为文件也存在于那里。如果指定路径在当前仓库中不存在,停止并报告该不匹配,而不是猜测其他工作区。

Rename / move by bindings, not text

通过绑定而非文本进行重命名/移动

The #1 way a "rename" silently corrupts code is editing textual matches (comments, strings, unrelated overloads) instead of real bindings. Find every binding reference first, then edit semantically. Use the strongest tool available: an IDE/Roslyn workspace refactoring, then the C# LSP the
dotnet
plugin declares
(
findReferences
,
goToDefinition
,
incomingCalls
,
rename
code action), then analyzer code-fixes / Roslynator, then compiler-validated edits (edit the true bindings, rebuild, let the compiler flag misses). Plain find/replace only when scope is provably tiny and every hit is verified. Include every
partial
declaration, and edit the generator input, never generated (
*.g.cs
) output.
For the operation → Roslyn-provider mapping and representative PRs, see references/operation-catalog.md.
“重命名”悄然破坏代码的头号方式是编辑文本匹配(注释、字符串、无关的重载),而不是真正的绑定。首先找到每个绑定引用,然后进行语义编辑。使用可用的最强工具:IDE/Roslyn 工作区重构,然后是
dotnet
插件声明
的 C# LSP(
findReferences
、
goToDefinition
、
incomingCalls
、
rename
代码操作),然后是分析器代码修复 / Roslynator,然后是编译器验证的编辑(编辑真正的绑定,重新构建,让编译器标记遗漏)。仅当范围被证明极小且每个命中都被验证时才使用纯查找/替换。包括每一个
partial
声明,并编辑生成器输入,而不是生成(
*.g.cs
)输出。
有关操作 → Roslyn 提供者映射和代表性 PR,请参阅 references/operation-catalog.md。

Consolidate toward the existing source of truth

向现有事实源整合

When de-duplicating, preserve the ownership direction stated by the code or request. If
B
duplicates an implementation already owned by
A
, keep
A
canonical and make
B
delegate to it; do not invert the dependency merely because either direction compiles. Preserve public compatibility wrappers when the duplicate surface is shipped, and migrate only in-repo callers that are safe to move.
去重时,保留代码或请求声明的所有权方向。如果
B
重复了
A
已经拥有的实现,保持
A
为规范版本,让
B
委托给它;不要仅仅因为任一方向都能编译就反转依赖关系。当重复表面被发布时,保留公共兼容包装器,并且仅迁移仓库内安全可移动的调用者。

Decisions that change the edit

改变编辑的决策

Use the first matching row instead of applying the requested operation mechanically:
SituationDoNever
Inline an internal, unshipped pass-through wrapperMigrate every binding reference to the target, remove the wrapper, then compile to catch misses.Keep dead indirection "for compatibility" when no compatibility boundary exists.
Inline or remove a shipped/public wrapperMigrate ordinary in-repo callers, but retain an
[Obsolete]
forwarding entry point unless the request explicitly authorizes a breaking change.
Delete a shipped API merely because all current source callers were migrated.
Rename a member reached by a string, reflection, DI, or configurationRename binding-based callers; preserve the observed external name with a forwarding shim or metadata, and exercise the old-name path.Rewrite an external/configured name just to make the new source name consistent.
Extract duplicated logic whose callers pass different valuesExtract the algorithm and pass each caller's existing inputs through unchanged.Collapse distinct inputs, evaluation order, rounding, or side effects into one caller's version.
Rename code compiled under
#if
or multiple TFMs
Update every source branch and validate each target framework explicitly.Treat a green default-target build as evidence for unbuilt branches.
Merge near-identical typesParameterize only the values that differ, migrate every construction site, and preserve each old value exactly. If the old types are internal/unshipped and the request says to merge into one type, delete their declarations.Retain unnecessary aliases, static holders, factories, or wrapper types that leave the requested merge incomplete; introduce a new hierarchy or behavior.
使用第一个匹配行,而不是机械地应用请求的操作:
情况做绝不
内联内部、未发布的透传包装器将所有绑定引用迁移到目标,移除包装器,然后编译以捕获遗漏。当不存在兼容性边界时,保留死间接层“以兼容”。
内联或移除已发布/公共包装器迁移普通仓库内调用者,但保留
[Obsolete]
转发入口点,除非请求明确授权破坏性更改。
仅仅因为所有当前源代码调用者都被迁移就删除已发布的 API。
重命名通过字符串、反射、DI 或配置访问的成员重命名基于绑定的调用者;使用转发 shim 或元数据保留观察到的外部名称,并测试旧名称路径。仅为了使新源代码名称一致而重写外部/配置的名称。
提取调用者传递不同值的重复逻辑提取算法,并让每个调用者现有的输入原样传入。将不同的输入、求值顺序、舍入或副作用折叠为某个调用者的版本。
重命名在
#if
或多个 TFM 下编译的代码
更新每个源代码分支,并显式验证每个目标框架。将绿色默认目标构建视为未构建分支的证据。
合并几乎相同的类型仅参数化不同的值,迁移每个构造点,并精确保留每个旧值。如果旧类型是内部/未发布的,并且请求说合并为一种类型,删除它们的声明。保留不必要的别名、静态持有者、工厂或包装器类型,使请求的合并不完整;引入新的层次结构或行为。

Preserve contracts beyond C# call sites

保持 C# 调用点之外的契约

Compilation proves binding compatibility, not every external contract. Before renaming or moving a type/member, check whether its name or metadata is observed by serialization, reflection, dependency injection, configuration binding, source generators, P/Invoke, or
dynamic
.
BoundaryRequired decision
Serialized/configuration namePreserve the external name with the repository's existing mechanism (for example,
JsonPropertyName
) while migrating C# callers; run a focused round-trip or payload test.
Public nullable annotationThe mandatory classification gate applies: leave it unchanged and hand off as a source-contract change.
Uncovered reflection or runtime lookupDo not guess that a compile-clean rename is safe. Preserve the observed name or stop and report the unverified runtime boundary.
编译证明绑定兼容性,而不是每个外部契约。在重命名或移动类型/成员之前,检查其名称或元数据是否被序列化、反射、依赖注入、配置绑定、源代码生成器、P/Invoke 或
dynamic
观察到。
边界必需决策
序列化/配置名称在迁移 C# 调用者的同时,使用仓库现有机制(例如
JsonPropertyName
)保留外部名称;运行有针对性的往返或负载测试。
公共可空注解强制分类门禁适用:保持原样,并作为源代码契约更改移交。
未覆盖的反射或运行时查找不要猜测编译干净的重命名是安全的。保留观察到的名称,或停止并报告未验证的运行时边界。

Verify proportionally

按比例验证

Confirm behavior is preserved after the edit — scaled to blast radius, not a fixed ceremony:
  • Local / private (method-local or
    private
    member, one file, single target framework, no public surface, no
    partial
    /generated/
    #if
    ): skip a separate baseline unless the tree is already suspect. Make the edit, then run the narrowest build and relevant tests once. Let the compiler catch missed references.
  • Cross-boundary (public/shipped symbol, multi-targeted project,
    #if
    /platform branches, or
    partial
    /generated code): establish a baseline, then run an explicit build and the relevant tests for each target framework after the edit (a test command's implicit build is not separate build evidence; a green default build can hide a break on another TFM), and run the hazards check below.
Use the repo's own build/test workflow when it documents one (
README
/
CONTRIBUTING
,
build.*
,
eng/
,
global.json
,
.github/workflows
); its instructions win over any generic command.
编辑后确认行为被保持——按爆炸半径缩放,而不是固定仪式:
  • 局部/私有(方法局部或
    private
    成员,单文件,单目标框架,无公共表面,无
    partial
    /生成/
    #if
    ):除非树已经可疑,否则跳过单独的基线。进行编辑,然后运行最窄的构建和相关测试一次。让编译器捕获遗漏的引用。
  • 跨边界(公共/已发布符号、多目标项目、
    #if
    /平台分支,或
    partial
    /生成代码):建立基线,然后在编辑后为每个目标框架显式运行构建和相关测试(测试命令的隐式构建不构成单独的构建证据;绿色默认构建可能掩盖另一个 TFM 上的损坏),并运行以下危险检查。
当仓库有文档化的构建/测试工作流时,使用其自己的构建/测试工作流(
README
/
CONTRIBUTING
、
build.*
、
eng/
、
global.json
、
.github/workflows
);其说明优先于任何通用命令。

Typical workflow (one operation)

典型工作流(单次操作)

  1. Choose one named refactoring operation and keep the step focused on that operation only.
  2. Find true binding references (
    findReferences
    /
    goToDefinition
    /rename) and include all
    partial
    declarations.
  3. Establish a baseline first only for a cross-boundary change or a tree not already known green.
  4. Apply the change via the most semantics-aware tool available; avoid blind find/replace when possible.
  5. Rebuild and run the relevant tests. If the gate goes red, report the failure and repair or reassess only your edit; never discard unrelated worktree changes.
Otherwise:
bash
dotnet build   # 0 errors
dotnet test    # stays green; same pass count as before
One operation per step; never mix a refactor and a behavior change in the same step. On red, stop and report the failure; repair only your edit without discarding unrelated worktree changes.
  1. 选择一个指定的重构操作,并保持步骤仅聚焦于该操作。
  2. 查找真正的绑定引用(
    findReferences
    /
    goToDefinition
    /rename),并包括所有
    partial
    声明。
  3. 仅对跨边界更改或尚未已知绿色的树首先建立基线。
  4. 使用最具语义感知的工具应用更改;尽可能避免盲目查找/替换。
  5. 重新构建并运行相关测试。如果门禁变红,报告失败并仅修复或重新评估您的编辑;绝不要丢弃无关的工作树更改。
否则:
bash
dotnet build   # 0 errors
dotnet test    # stays green; same pass count as before
每一步一个操作;绝不在同一步骤中混合重构和行为更改。变红时,停止并报告失败;仅修复您的编辑,不丢弃无关的工作树更改。

Final response contract

最终响应契约

Keep the handoff concise and evidence-based:
  • Refactor: name the structural operation and the symbols/files changed.
  • Preserved: name the behavior or compatibility boundary and the mechanism that preserved it.
  • Validation: report the exact commands and observed result; never claim success after a failed restore, build, target framework, or test run.
  • Deferred: for a mixed request, name the behavior/contract change intentionally left undone and its correct next workflow. Omit this line when nothing was deferred.
保持移交简洁且基于证据:
  • 重构: 命名结构操作和更改的符号/文件。
  • 保持: 命名行为或兼容性边界以及保持它的机制。
  • 验证: 报告确切的命令和观察到的结果;在恢复、构建、目标框架或测试运行失败后绝不声称成功。
  • 推迟: 对于混合请求,命名有意未完成的行为/契约更改及其正确的下一步工作流。当没有推迟任何内容时省略此行。

Cross-boundary hazards (only when it touches a boundary)

跨边界危险(仅在触及边界时)

If — and only if — the change touches a public symbol, a multi-targeted project, or
partial
/generated code, some breaks won't show up as a failing test. Search the repo for the surface that governs the symbol (don't assume): the public-API gate (
PublicAPI.Shipped/Unshipped.txt
for PublicApiAnalyzers, and/or
ApiCompat
/
<EnablePackageValidation>
— not interchangeable),
<TargetFrameworks>
/
#if
branches, and
InternalsVisibleTo
. Moving a public type to another assembly needs
[TypeForwardedTo]
in the original assembly; a move within one assembly does not. A public rename needs an
[Obsolete]
shim, not a forwarder. For a provably local/private change, skip these checks.
当且仅当更改触及公共符号、多目标项目或
partial
/生成代码时,某些破坏不会显示为测试失败。搜索仓库中支配该符号的表面(不要假设):公共 API 门禁(PublicApiAnalyzers 的
PublicAPI.Shipped/Unshipped.txt
,和/或
ApiCompat
/
<EnablePackageValidation>
——不可互换)、
<TargetFrameworks>
/
#if
分支,以及
InternalsVisibleTo
。将公共类型移动到另一个程序集需要在原始程序集中使用
[TypeForwardedTo]
;在同一程序集内移动则不需要。公共重命名需要
[Obsolete]
shim,而不是转发器。对于可证明的局部/私有更改,跳过这些检查。

Stop and ask when

何时停止并询问

  • The baseline is already red (you can't prove you preserved behavior).
  • A public/shipped API would change without a forwarder/shim or explicit authorization for a breaking change.
  • Equivalence depends on runtime behavior tests don't cover (reflection, DI, serialization,
    dynamic
    , P/Invoke) — flag it.
  • 基线已经是红色的(您无法证明您保持了行为)。
  • 公共/已发布 API 会更改而没有转发器/shim 或明确授权破坏性更改。
  • 等价性取决于测试未覆盖的运行时行为(反射、DI、序列化、
    dynamic
    、P/Invoke)——标记它。