33// See the LICENSE file in the project root for more information.
44
55using System . Diagnostics ;
6+ using System . Runtime . CompilerServices ;
67
78namespace System
89{
10+ /// <summary>Represent a type can be used to index a collection either from the start or the end.</summary>
11+ /// <remarks>
12+ /// Index is used by the C# compiler to support the new index syntax
13+ /// <code>
14+ /// int[] someArray = new int[5] { 1, 2, 3, 4, 5 } ;
15+ /// int lastElement = someArray[^1]; // lastElement = 5
16+ /// </code>
17+ /// </remarks>
918 public readonly struct Index : IEquatable < Index >
1019 {
1120 private readonly int _value ;
1221
13- public Index ( int value , bool fromEnd )
22+ /// <summary>Construct an Index using a value and indicating if the index is from the start or from the end.</summary>
23+ /// <param name="value">The index value. it has to be zero or positive number.</param>
24+ /// <param name="fromEnd">Indicating if the index is from the start or from the end.</param>
25+ /// <remarks>
26+ /// If the Index constructed from the end, index value 1 means pointing at the last element and index value 0 means pointing at beyond last element.
27+ /// </remarks>
28+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
29+ public Index ( int value , bool fromEnd = false )
1430 {
1531 if ( value < 0 )
1632 {
1733 ThrowHelper . ThrowValueArgumentOutOfRange_NeedNonNegNumException ( ) ;
1834 }
1935
20- _value = fromEnd ? ~ value : value ;
36+ if ( fromEnd )
37+ _value = ~ value ;
38+ else
39+ _value = value ;
2140 }
2241
23- public int Value => _value < 0 ? ~ _value : _value ;
24- public bool FromEnd => _value < 0 ;
42+ // The following private constructors mainly created for perf reason to avoid the checks
43+ private Index ( int value )
44+ {
45+ _value = value ;
46+ }
47+
48+ /// <summary>Create an Index pointing at first element.</summary>
49+ public static Index Start => new Index ( 0 ) ;
50+
51+ /// <summary>Create an Index pointing at beyond last element.</summary>
52+ public static Index End => new Index ( ~ 0 ) ;
53+
54+ /// <summary>Create an Index from the start at the position indicated by the value.</summary>
55+ /// <param name="value">The index value from the start.</param>
56+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
57+ public static Index FromStart ( int value )
58+ {
59+ if ( value < 0 )
60+ {
61+ ThrowHelper . ThrowValueArgumentOutOfRange_NeedNonNegNumException ( ) ;
62+ }
63+
64+ return new Index ( value ) ;
65+ }
66+
67+ /// <summary>Create an Index from the end at the position indicated by the value.</summary>
68+ /// <param name="value">The index value from the end.</param>
69+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
70+ public static Index FromEnd ( int value )
71+ {
72+ if ( value < 0 )
73+ {
74+ ThrowHelper . ThrowValueArgumentOutOfRange_NeedNonNegNumException ( ) ;
75+ }
76+
77+ return new Index ( ~ value ) ;
78+ }
79+
80+ /// <summary>Returns the index value.</summary>
81+ public int Value
82+ {
83+ get
84+ {
85+ if ( _value < 0 )
86+ return ~ _value ;
87+ else
88+ return _value ;
89+ }
90+ }
91+
92+ /// <summary>Indicates whether the index is from the start or the end.</summary>
93+ public bool IsFromEnd => _value < 0 ;
94+
95+ /// <summary>Calculate the offset from the start using the giving collection length.</summary>
96+ /// <param name="length">The length of the collection that the Index will be used with. length has to be a positive value</param>
97+ /// <remarks>
98+ /// For performance reason, we don't validate the input length parameter and the returned offset value against negative values.
99+ /// we don't validate either the returned offset is greater than the input length.
100+ /// It is expected Index will be used with collections which always have non negative length/count. If the returned offset is negative and
101+ /// then used to index a collection will get out of range exception which will be same affect as the validation.
102+ /// </remarks>
103+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
104+ public int GetOffset ( int length )
105+ {
106+ int offset ;
107+
108+ if ( IsFromEnd )
109+ offset = length - ( ~ _value ) ;
110+ else
111+ offset = _value ;
112+
113+ return offset ;
114+ }
115+
116+ /// <summary>Indicates whether the current Index object is equal to another object of the same type.</summary>
117+ /// <param name="value">An object to compare with this object</param>
25118 public override bool Equals ( object value ) => value is Index && _value == ( ( Index ) value ) . _value ;
119+
120+ /// <summary>Indicates whether the current Index object is equal to another Index object.</summary>
121+ /// <param name="other">An object to compare with this object</param>
26122 public bool Equals ( Index other ) => _value == other . _value ;
27123
28- public override int GetHashCode ( )
124+ /// <summary>Returns the hash code for this instance.</summary>
125+ public override int GetHashCode ( ) => _value ;
126+
127+ /// <summary>Converts integer number to an Index.</summary>
128+ public static implicit operator Index ( int value ) => FromStart ( value ) ;
129+
130+ /// <summary>Converts the value of the current Index object to its equivalent string representation.</summary>
131+ public override string ToString ( )
29132 {
30- return _value ;
31- }
133+ if ( IsFromEnd )
134+ return ToStringFromEnd ( ) ;
32135
33- public override string ToString ( ) => FromEnd ? ToStringFromEnd ( ) : ( ( uint ) Value ) . ToString ( ) ;
136+ return ( ( uint ) Value ) . ToString ( ) ;
137+ }
34138
35139 private string ToStringFromEnd ( )
36140 {
@@ -41,7 +145,5 @@ private string ToStringFromEnd()
41145 return new string ( span . Slice ( 0 , charsWritten + 1 ) ) ;
42146 }
43147
44- public static implicit operator Index ( int value )
45- => new Index ( value , fromEnd : false ) ;
46148 }
47149}
0 commit comments