Found by: the issue #201 oracle sweep, docs/notes/oracle-sweep-2026-07-10.md; catalogued as docs/notes/field-notes-patterns.md entry 11.
The pattern
A property-changed callback (a PropertyChangedCallback or a plain virtual On<X>Changed(old, new) override) manages a subscription across property changes: detach from the old value, attach to the new one, same handler.
private static void OnCommandChanged(CommandTriggerAction action, DependencyPropertyChangedEventArgs e)
{
if (e.OldValue is ICommand oldCommand)
oldCommand.CanExecuteChanged -= action.OnCommandCanExecuteChanged; // unsub OLD
if (e.NewValue is ICommand newCommand)
newCommand.CanExecuteChanged += action.OnCommandCanExecuteChanged; // sub NEW <- flagged OWN001
}
Own.NET pairs a += with a -= per source variable — newCommand has no -= written against it — so it doesn't see that the oldCommand unsubscribe a few lines above is the same rotation slot. There's no leak: at most one subscription is ever live.
Evidence — confirmed in 3 separate real repos, 6+ call sites
- MahApps.Metro
src/MahApps.Metro/Actions/CommandTriggerAction.cs:102-117
- MaterialDesignInXamlToolkit
src/MaterialDesignThemes.Wpf/SmartHint.cs:189-209 (4 findings — IsVisibleChanged/ContentChanged/Loaded/FocusedChanged, same rotation)
- AvalonEdit
ICSharpCode.AvalonEdit/Editing/AbstractMargin.cs:92-101, LineNumberMargin.cs:105-118, Folding/FoldingMargin.cs (a plain virtual OnTextViewChanged(old, new) override, not even a DP callback — same shape)
This is the single most-corroborated false-positive class from the sweep — worth the highest priority among the follow-ups it produced.
Suggested direction (not prescriptive)
Recognise a -=/+= pair as one paired lifecycle when they're against the old/new halves of the same DP-changed callback (or an equivalent On<X>Changed(TValue oldValue, TValue newValue) virtual-method override): same handler expression, unsub on a variable bound from the "old" parameter/branch, sub on a variable bound from the "new" one, within the same method.
Scope
No analyzer code changed as part of the sweep (per the sweep's own guardrails) — this issue exists to track the fix as its own unit of work.
Found by: the issue #201 oracle sweep,
docs/notes/oracle-sweep-2026-07-10.md; catalogued asdocs/notes/field-notes-patterns.mdentry 11.The pattern
A property-changed callback (a
PropertyChangedCallbackor a plain virtualOn<X>Changed(old, new)override) manages a subscription across property changes: detach from the old value, attach to the new one, same handler.Own.NET pairs a
+=with a-=per source variable —newCommandhas no-=written against it — so it doesn't see that theoldCommandunsubscribe a few lines above is the same rotation slot. There's no leak: at most one subscription is ever live.Evidence — confirmed in 3 separate real repos, 6+ call sites
src/MahApps.Metro/Actions/CommandTriggerAction.cs:102-117src/MaterialDesignThemes.Wpf/SmartHint.cs:189-209(4 findings —IsVisibleChanged/ContentChanged/Loaded/FocusedChanged, same rotation)ICSharpCode.AvalonEdit/Editing/AbstractMargin.cs:92-101,LineNumberMargin.cs:105-118,Folding/FoldingMargin.cs(a plain virtualOnTextViewChanged(old, new)override, not even a DP callback — same shape)This is the single most-corroborated false-positive class from the sweep — worth the highest priority among the follow-ups it produced.
Suggested direction (not prescriptive)
Recognise a
-=/+=pair as one paired lifecycle when they're against the old/new halves of the same DP-changed callback (or an equivalentOn<X>Changed(TValue oldValue, TValue newValue)virtual-method override): same handler expression, unsub on a variable bound from the "old" parameter/branch, sub on a variable bound from the "new" one, within the same method.Scope
No analyzer code changed as part of the sweep (per the sweep's own guardrails) — this issue exists to track the fix as its own unit of work.