You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
InOpenRange and InClosedRange have inverted semantics (endpoint behavior is swapped vs. the names) #3
InOpenRange and InClosedRange have inverted endpoint semantics — each one is the mathematical definition of the other. By the standard convention an open interval excludes its endpoints (from, to) and a closed interval includes them [from, to]. The implementations are reversed:
// Extensions.Standard/Utilities.cspublicstaticboolInClosedRange<T>(thisTvalue,Tfrom,Tto)whereT:IComparable<T>{returnvalue.CompareTo(from)>0&&value.CompareTo(to)<0;// strict ends → this is an OPEN interval (from, to)}publicstaticboolInOpenRange<T>(thisTvalue,Tfrom,Tto)whereT:IComparable<T>{returnvalue.CompareTo(from)>=0&&value.CompareTo(to)<=0;// inclusive ends → this is a CLOSED interval [from, to]}
So InOpenRange returns trueon the boundary and InClosedRange returns falseon the boundary — the opposite of what the names promise.
Reproduction
10.InOpenRange(0,10);// True — but "open" should exclude 100.InOpenRange(0,10);// True — but "open" should exclude 00d.InOpenRange(0,0);// True10.InClosedRange(0,10);// False — but "closed" should include 105.InClosedRange(0,10);// True (only the interior)
The existing unit tests (ExtensionsTest.InOpenRangeTest2, InClosedRangeTest2, the *Dates variants) assert exactly this inverted behavior, so the test suite passes while codifying the swap.
Impact
This is a quiet footgun: the names read as precise mathematical terms, so callers reasonably assume InOpenRange excludes the endpoints. In a downstream project a price-fill range check price.InOpenRange(low, high) was assumed to be exclusive; it is actually inclusive, which silently invalidated a bug analysis built on that assumption. Anyone reaching for "open interval" semantics gets the closed one (and vice versa) with no compiler help.
Suggested fix
A straight swap of the two bodies is the correct behavior but is binary-breaking for existing callers who have adapted to the current behavior. Options, least-disruptive first:
Add correctly-named methods and deprecate the misleading ones. Introduce e.g. InRangeExclusive / InRangeInclusive (or Between / BetweenInclusive) with the obvious semantics, and mark InOpenRange / InClosedRange[Obsolete(...)] pointing at the replacements. Non-breaking.
Document the inversion in XML doc comments on both methods (<summary>/<remarks>) so IntelliSense warns readers, even if the names stay.
Swap the bodies and ship a major version bump, updating the tests — the clean fix, but breaking.
Happy to send a PR for whichever direction you prefer.
Environment
Package: Extensions.Standard 8.1.0 (current on master / NuGet)
Rather than silently flip the behavior of the existing methods (which would break callers relying on the current semantics without any compile-time signal), we:
InRangeExclusive — open interval (from, to), endpoints excluded.
Marked InClosedRange / InOpenRange[Obsolete] with guidance to the new methods. Their behavior is unchanged, so existing code keeps working but now gets a deprecation warning pointing at the correct replacement. They'll be removed in a future major version.
Tests cover the new methods' semantics, and the deprecated shims are retained as regression coverage.
Activity
PFalkowski commentedon Jun 13, 2026
Resolved in #4, shipped in 10.0.0.
Rather than silently flip the behavior of the existing methods (which would break callers relying on the current semantics without any compile-time signal), we:
InRangeInclusive— closed interval[from, to], endpoints included.InRangeExclusive— open interval(from, to), endpoints excluded.InClosedRange/InOpenRange[Obsolete]with guidance to the new methods. Their behavior is unchanged, so existing code keeps working but now gets a deprecation warning pointing at the correct replacement. They'll be removed in a future major version.Tests cover the new methods' semantics, and the deprecated shims are retained as regression coverage.