Class NDScopedAttribute
- Namespace
- NumSharp
- Assembly
- NumSharp.dll
Marks a method (or a property accessor) as an NDScope boundary: at build
time the NumSharp IL weaver (tools/NumSharp.Build, shipped to consumer projects
as the NumSharp.Build NuGet package) injects the exact code the
hand-written pattern spells —
using var scope = NDScope.Open();
...original body, byte-for-byte...
return scope.Returns(result); // NDArray-like returns
— so the source keeps its 100% original body and the reclamation is invisible.
[AttributeUsage(AttributeTargets.Method|AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public sealed class NDScopedAttribute : Attribute
- Inheritance
-
NDScopedAttribute
- Inherited Members
Remarks
What the weaver injects (see DISPOSAL-GUIDELINES.md → "The weaver"):
a scope local assigned from Open() before the original first
instruction; the whole original body wrapped in try/finally with
Dispose() in the finally; every ret routed through
Returns<T>(T) for NDArray-like returns
(Returns<T>(T[]) for array returns, the typed Returns tuple
overloads for a small all-NDArray ValueTuple and Returns(ITuple) for any other
ValueTuple/Tuple — any arity up to 8, mixed components — Returns(IArraySlice)/
Returns(UnmanagedStorage) for a bare lower-layer buffer return, and
YieldTo(NDScope) for a result-struct carrier), and every
out NDArray parameter's final value yielded before each successful return.
Synchronous iterators are woven too — an IEnumerable<T>/
IEnumerator<T> method (yield return without await) compiles to a
state machine, so the stub keeps only the attribute and the scope is held in a weaver-added
state-machine field: ONE scope for the whole enumeration, suspended between MoveNext
calls and reclaimed at the final MoveNext or the enumerator's Dispose() (an
early break out of a foreach included), with every yield returned
element routed through the same Returns/YieldTo egress the consumer owns.
Async methods, async iterators and non-async Task/ValueTask returns
use NDScopedAsyncAttribute instead — those suspend across await (or
defer disposal to a task's completion) and need the deferral seam. Marking one of them
[NDScoped] is a build ERROR (NDW009) that names the right attribute, never a silent
unwoven ship.
The weaver REJECTS (build error NDW003) only shapes whose egress it cannot see:
ref NDArray parameters, and an UNSUPPORTED carrier — a bespoke reference type, a
collection, or a result struct that does NOT implement INDArrayCarrier —
whether returned directly or yield returned; scope those by hand (NDW004 = an
unrecognized, non-C# state-machine shape; NDW008 = the referenced NumSharp predates the
iterator scope seam).
A method whose body already opens an NDScope is skipped (idempotence), so hand-scoped code may carry the attribute without double-wrapping.
On a virtual, abstract or interface member the attribute is a CONTRACT its
inheritors inherit. Every override and every implementation (implicit or explicit,
through a generic base or interface, in this assembly or in a consumer assembly overriding a
NumSharp member) is woven exactly as if it carried the attribute itself — the declaration
decides the scoping, the implementation keeps its 100% original body. The nearest
declaration up the override/implementation chain wins, and an override that declares its OWN
scope-family attribute keeps it: [NDScoped]/[NDScopedAsync] re-state or change
the model, NDScopedCoveredAttribute opts the override OUT of the inherited
weave (its transients then ride the caller's ambient scope — the author's assertion, as for
any covered helper). A body-less attributed declaration (abstract method, interface member,
abstract property) is never itself woven and never an error — NDW005 is reserved for an
extern, which has neither a body nor inheritors. The compile-time analyzer applies the
same resolution: an inheriting override draws no NDW012 for its transients, and its target
gate (NDW002–NDW011) reads its declaration's attribute. Inherited = true below states the
same for reflection (GetCustomAttribute(inherit: true) finds the base declaration's
attribute on an override); the weaver and analyzer implement the inheritance themselves —
they read metadata, not reflection.
PUBLIC because the attribute is consumer-facing: a project that installs the
NumSharp.Build package marks its own composition methods with it. Without the
package the attribute is inert metadata — the method runs unscoped, the pre-weave
finalizer-backstop behaviour.