Collections, selection and lookup
Strict/Try helpers use the opt-in DirectSharp.Collections namespace. Include import Global.DirectSharp.Collections in the relevant file/project. Counts and positions are Integer. If LINQ is also equally applicable, use explicit static qualification; see export declarations and binding.
Use the DirectSharp library contracts deliberately; imported .NET/LINQ APIs retain their own behavior. The library reference supplies full timing, ownership and failure details.
First and last values
DirectSharp's recorded First/Last accessor forms return nothing for an empty sequence. A real zero, false or empty String remains that value. Imported LINQ First()/Last() throw on empty input; FirstOrDefault()/LastOrDefault() use CLR defaults and can conflate an empty value-type sequence with a default-valued item. Select the intended native operation explicitly when using .NET APIs. See the C# absence account for nullable projection and private lowering strategies, and the API catalogue for the remaining export/binding boundary.
For a known value element type, selection needs an actual Nullable result; reference and already-nullable elements retain their own absence-capable representation. A fixed unconstrained CLR T return is not automatically nullable. The bound C# strategies and Invoice example show those cases.
Do not infer the helper contract from spelling alone. Empty parentheses can denote a property read only as the property fallback rule permits. A selected actual LINQ method keeps its normal failures. Neither a public generic helper export nor asynchronous-source applicability is established by that tolerance.
Inclusive sequence selection
DirectSharp strict Take/Skip/TakeLast reject a nothing source immediately with ArgumentNullException before numeric validation. Their Try counterparts return nothing immediately for a nothing source, even with zero or invalid numeric inputs. Present-source traversal, bounds and source-error rules remain separate. See CS-SEQUENCE-011.
The DirectSharp library's items.Take(start, end) requires the complete zero-based inclusive interval and returns a deferred streaming IEnumerable<T>. Negative positions or end below start throw ArgumentOutOfRangeException at the call. A short source, including a start beyond its length, throws during enumeration; a valid prefix may already have been yielded. Early consumer exit need not discover a later shortage. No Count prepass or implicit snapshot is supplied. Source failures propagate and enumerators are disposed normally.
Its tolerant counterpart TryTake(start, end) normalizes negative positions to zero and clips an oversized end to the last item. An interval still reversed after adjustment returns nothing without reading the source. Otherwise TryTake traverses once at the call through the requested end or source exhaustion and collects only selected items, with no Count pass or restart. No selected items means nothing, including a start beyond the source or an empty source. The nullable IReadOnlyList result exposes Count/index access and reuses the collected values without re-enumeration; source failures propagate at the call and its enumerator is disposed before returning. Both are library helpers; imported LINQ Take(count)/Take(Range) retain their own contracts.
| Call over items 0 through 6 | Result |
|---|---|
items.Take(-3, 2) |
ArgumentOutOfRangeException at the call |
items.Take(10, 5) |
ArgumentOutOfRangeException at the call |
items.Take(5, 10) |
5, 6, then ArgumentOutOfRangeException if enumeration continues |
items.TryTake(-3, 2) |
0, 1, 2 |
items.TryTake(5, 10) |
5, 6 |
items.TryTake(10, 5) |
nothing |
items.TryTake(20, 30) |
nothing using available length or one traversal |
Known array Length or collection Count can optimize selection without counting by enumeration. Arrays and ordinary lists may copy selected positions directly; custom collection interfaces alone do not authorize bypassing observable enumeration behavior. Preserve TryTake collection/absence and strict Take's deferred prefix/error timing.
For strict Take, each enumeration obtains a fresh source enumerator and can observe later source changes. No separate Count pass or implicit snapshot occurs. Dispose the enumerator on completion, failure or early consumer exit, stop after the inclusive interval and retain ordinary source failures. Present empty sequences remain present values: ?? throw checks absence; require/ensure can check non-emptiness. Materialize explicitly when checking and using the selection must share one snapshot. See the library examples and reference lowering.
Count-taking sequence helpers
For DirectSharp-bound Take(count), Skip(count) and TakeLast(count), negative count throws ArgumentOutOfRangeException at the call and insufficient source items throw during deferred traversal. Take can yield a prefix before discovering a shortage. Skip requires count items to skip; skipping exactly the source length succeeds with present empty. TakeLast reads to source exhaustion before yielding its tail on a general enumerable, and fails if the tail is shorter than count. Strict Take(0)/TakeLast(0) are present empty; Skip(0) yields the source.
TryTake(count), TryTakeLast(count) and TrySkip(count) normalize negatives to zero, use available items and collect their result at the call. No selected items means actual nothing, including normalized zero for TryTake/TryTakeLast or no remainder for TrySkip. A nullable IReadOnlyList result is reused without rereading source; source failures propagate rather than becoming absence. TryTake reads a bounded prefix, while TryTakeLast/TrySkip need source exhaustion without suitable metadata. TrySkip may retain a large remainder and cannot return for an unbounded source. No enumerating Count prepass is supplied; safe available metadata remains permitted.
| Call over items 0 through 6 | Result |
|---|---|
items.Take(10) / items.Skip(10) / items.TakeLast(10) |
ArgumentOutOfRangeException on traversal |
items.Skip(7) |
Present empty |
items.TryTake(10) / items.TryTakeLast(10) |
All seven items |
items.TryTake(0) / items.TryTakeLast(-2) |
nothing |
items.TrySkip(5) |
5, 6 |
items.TrySkip(7) / items.TrySkip(10) |
nothing |
These contracts do not replace imported LINQ behavior. See CS-SEQUENCE-007–010 for binding, timing and reference patterns.
Indexed lookup and map insertion
| Operation | Missing receiver/location | Present location |
|---|---|---|
items[index] / map[key] |
Ordinary null/index/key failure | Return stored value |
try_get map[key] |
Covered access failure becomes nothing |
Return stored value |
map[key] ??= expression |
Missing key triggers insertion; unrelated failures propagate | Fill nothing; retain any other value |
Rules CS-INDEX-001–CS-INDEX-006 distinguish read failure from assignment intent. Map updates evaluate target expressions once and defer the RHS until needed. Ordinary map ??= supplies no atomic concurrency guarantee. Enumeration contracts are described in async and concurrency.
Chapter 15 includes the dictionary TryGetValue lowering. It must not be generalized to arbitrary custom indexers or used to make every indexed read silent.