Skip to main content

revit-extensions-backward-compat

Deprecate or replace a public member of Nice3point.Revit.Extensions without breaking downstream consumers, using [Obsolete] with a JetBrains CodeTemplate auto-conversion pattern. USE FOR: renaming or superseding an existing extension while keeping the old member functional; retiring a member that Revit itself deprecated in a specific year; deciding what may change on the public surface and how to migrate callers automatically. DO NOT USE FOR: authoring a brand-new extension from scratch — use revit-extensions-authoring.

설치로 이동

소스 정보

저장소
Nice3point/RevitExtensions
최근 소스 활동
2026년 7월 23일 20:16
감지된 SKILL.md 언어
영어
스타
168
포크
44

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
revit-extensions-backward-compat
description
Deprecate or replace a public member of Nice3point.Revit.Extensions without breaking downstream consumers, using [Obsolete] with a JetBrains CodeTemplate auto-conversion pattern. USE FOR: renaming or superseding an existing extension while keeping the old member functional; retiring a member that Revit itself deprecated in a specific year; deciding what may change on the public surface and how to migrate callers automatically. DO NOT USE FOR: authoring a brand-new extension from scratch — use revit-extensions-authoring.
license
MIT
# Preserving Backward Compatibility in Revit Extensions Nice3point.Revit.Extensions is a public NuGet library with downstream consumers; its public surface is a contract. A deleted or altered member breaks other people's builds. Keep the old member forever, mark it `[Obsolete]`, and ship a JetBrains `CodeTemplate`; ReSharper and Rider then auto-migrate call sites to the replacement. This skill covers deprecating, renaming, or superseding an existing member; it depends on JetBrains annotations (`JetBrains.Annotations`) for both `[Obsolete]`'s companion `CodeTemplate` and the surrounding attributes. ## When to use - Renaming an extension or replacing it with a better-named member while keeping every existing call compiling. - Retiring a member whose underlying Revit API was deprecated in a specific Revit year. - Deciding whether a proposed change to a signature, return type, or parameter list is allowed at all. ## When not to use - Adding a member that does not yet exist on the public surface — that is new-surface work; use revit-extensions-authoring. ## Workflow ### Step 1: Add the replacement beside the original, both wrapping the raw API Keep the old member in place and add the new one in the same `extension(Type)` block. Both members call the underlying Revit API directly; the new member never delegates to the old one, and the old one never delegates to the new one. Write full method bodies; an expression-bodied property is fine for a read-only value. ```csharp extension(Element element) { /// <summary>Determines whether element can be mirrored</summary> /// <returns>True if the element can be mirrored</returns> public bool CanBeMirrored => ElementTransformUtils.CanMirrorElement(element.Document, element.Id); } ``` ### Step 2: Mark the original `[Obsolete]` with a message that names the replacement The message states plainly what to use instead. The obsolete member keeps calling the original Revit API; it stays correct and independent and must not recurse into the new extension. ```csharp [Pure] [Obsolete("Use CanBeMirrored() instead")] public bool CanMirrorElement() { return ElementTransformUtils.CanMirrorElement(element.Document, element.Id); } ``` ### Step 3: Attach the CodeTemplate auto-conversion pattern Add `[CodeTemplate]` beside `[Obsolete]` so the IDE rewrites call sites automatically. `searchTemplate` matches the old call, `ReplaceTemplate` produces the new call, and the two `Message`/`ReplaceMessage` strings surface in the inspection and the quick-fix. Use `$expr$` for the receiver and match the replacement shape exactly — a property replacement drops the parentheses. ```csharp [Pure] [Obsolete("Use CanBeMirrored() instead")] [CodeTemplate( searchTemplate: "$expr$.CanMirrorElement()", Message = "CanMirrorElement is obsolete, use CanBeMirrored instead", ReplaceTemplate = "$expr$.CanBeMirrored", ReplaceMessage = "Replace with CanBeMirrored")] public bool CanMirrorElement() { return ElementTransformUtils.CanMirrorElement(element.Document, element.Id); } ``` When the member takes arguments, thread them through the template with named placeholders; the rewrite preserves them: ```csharp [Pure] [Obsolete("Use CanBeMirrored() instead")] [CodeTemplate( searchTemplate: "$expr$.CanMirrorElements($document$)", Message = "CanMirrorElements is obsolete, use CanBeMirrored instead", ReplaceTemplate = "$expr$.CanBeMirrored($document$)", ReplaceMessage = "Replace with CanBeMirrored()")] public bool CanMirrorElements(Document document) { return ElementTransformUtils.CanMirrorElements(document, elements); } ``` ### Step 4: Gate the deprecation when Revit deprecates the API in a specific year If the underlying Revit API is only obsolete from a given release, wrap just the `[Obsolete]` and `[CodeTemplate]` attributes in the matching `#if`; the member deprecates on the versions that warrant it and stays clean elsewhere. When the replacement adds a parameter, add it as an optional argument at the end; use a `$placeholder$` in the template for the new argument. ```csharp [Pure] #if REVIT2027_OR_GREATER [Obsolete("This method is deprecated in Revit 2027 and may be removed in a later version of Revit. We suggest you use the overload which accepts a region input instead.")] [CodeTemplate( searchTemplate: "$expr$.DownloadParameterOptions()", Message = "DownloadParameterOptions() is obsolete, use overload with region parameter", ReplaceTemplate = "$expr$.DownloadParameterOptions($arg$)", ReplaceMessage = "Replace with DownloadParameterOptions(region)")] #endif public ParameterDownloadOptions DownloadParameterOptions() { return ParameterUtils.DownloadParameterOptions(typeId); } ``` ### Step 5: Confirm nothing on the surface actually broke Hold every change to the allowed set before you compile: - Never change an existing signature or return type — the only allowed return-type change is a `void` mutation becoming its source object for chaining. - Add new parameters only as optional, and only at the end of the list. - Keep the old member functional indefinitely; deprecation is not deletion. Then compile every configuration from the `build` directory and update the shipped docs in the same change. ```shell dotnet run -c Release ``` Update `Readme.md`, add a categorized `Changelog.md` entry for the deprecation, and keep the XML docs on both the old and new members. ## Validation - [ ] The original member still exists, still compiles, and still calls the original Revit API, not the new member. - [ ] `[Obsolete]` carries a message that names the replacement; no `[EditorBrowsable(EditorBrowsableState.Never)]` sits on an obsolete `Element` extension. - [ ] `[CodeTemplate]` supplies `searchTemplate`, `ReplaceTemplate`, `Message`, and `ReplaceMessage`, and the replacement shape matches the new member (property vs. method, argument placeholders preserved). - [ ] A Revit-year-specific deprecation wraps only the attributes in the matching `#if`. - [ ] No signature or return type changed; any new parameter is optional and last; `dotnet run -c Release` compiles every configuration and `Readme.md`/`Changelog.md`/XML docs are updated. ## Common Pitfalls | Pitfall | Correct approach | |------------------------------------------------------------------------|------------------------------------------------------------------------------------------------| | Deleting or renaming the old member outright | Keep it, mark it `[Obsolete]`, and add the replacement beside it. | | The obsolete member calling the new extension | Call the original Revit API from the obsolete member; it never recurses and stays independent. | | `[EditorBrowsable(Never)]` on an obsolete `Element` extension | Omit it; let the member stay discoverable while `[Obsolete]` warns. | | `[Obsolete]` without a `[CodeTemplate]` | Add the `searchTemplate` → `ReplaceTemplate` pattern; callers auto-migrate. | | `ReplaceTemplate` keeping parentheses for a property replacement | Match the replacement exactly — drop the `()` when the new member is a property. | | Changing a signature or return type, or inserting a required parameter | Add a new member or an optional trailing parameter instead. | | Deprecating unconditionally when Revit deprecates only in a later year | Wrap just the `[Obsolete]`/`[CodeTemplate]` attributes in the matching `#if`. |
GitHub에서 보기