Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit 42bdcb7

Browse files
authored
Implementing ItemRef API Proposal (C# 7.2 constructs to S.C.I) (#25738)
* Add ItemRef() to ImmutableSortedSet and ImmutableArray * Add ItemRef() to ImmutableList * Add PeekRef() to ImmutableStack and ImmutableQueue * Add ValueRef() to ImmutableSortedDictionary and Builder * Fix tests * Test: Multitargeting 1.3 and 1.0 * Multitargeting comments * Fixed build break * Make ImmutableArray<T> a readonly struct * PR Comments * Fix build break
1 parent 3e6ecac commit 42bdcb7

30 files changed

+574
-2
lines changed

src/System.Collections.Immutable/ref/Configurations.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
<PropertyGroup>
44
<PackageConfigurations>
55
netstandard1.0;
6+
netstandard1.3;
67
netstandard;
78
</PackageConfigurations>
89
<BuildConfigurations>

src/System.Collections.Immutable/ref/System.Collections.Immutable.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,9 @@ public partial struct ImmutableArray<T> : System.Collections.Generic.ICollection
101101
public bool IsDefaultOrEmpty { get { throw null; } }
102102
public bool IsEmpty { get { throw null; } }
103103
public T this[int index] { get { throw null; } }
104+
#if FEATURE_ITEMREFAPI
105+
public ref readonly T ItemRef(int index) { throw null; }
106+
#endif
104107
public int Length { get { throw null; } }
105108
int System.Collections.Generic.ICollection<T>.Count { get { throw null; } }
106109
bool System.Collections.Generic.ICollection<T>.IsReadOnly { get { throw null; } }
@@ -198,6 +201,9 @@ internal Builder() { }
198201
public int Capacity { get { throw null; } set { } }
199202
public int Count { get { throw null; } set { } }
200203
public T this[int index] { get { throw null; } set { } }
204+
#if FEATURE_ITEMREFAPI
205+
public ref readonly T ItemRef(int index) { throw null; }
206+
#endif
201207
bool System.Collections.Generic.ICollection<T>.IsReadOnly { get { throw null; } }
202208
public void Add(T item) { }
203209
public void AddRange(System.Collections.Generic.IEnumerable<T> items) { }
@@ -524,6 +530,9 @@ internal ImmutableList() { }
524530
public int Count { get { throw null; } }
525531
public bool IsEmpty { get { throw null; } }
526532
public T this[int index] { get { throw null; } }
533+
#if FEATURE_ITEMREFAPI
534+
public ref readonly T ItemRef(int index) { throw null; }
535+
#endif
527536
bool System.Collections.Generic.ICollection<T>.IsReadOnly { get { throw null; } }
528537
T System.Collections.Generic.IList<T>.this[int index] { get { throw null; } set { } }
529538
bool System.Collections.ICollection.IsSynchronized { get { throw null; } }
@@ -610,6 +619,9 @@ public sealed partial class Builder : System.Collections.Generic.ICollection<T>,
610619
internal Builder() { }
611620
public int Count { get { throw null; } }
612621
public T this[int index] { get { throw null; } set { } }
622+
#if FEATURE_ITEMREFAPI
623+
public ref readonly T ItemRef(int index) { throw null; }
624+
#endif
613625
bool System.Collections.Generic.ICollection<T>.IsReadOnly { get { throw null; } }
614626
bool System.Collections.ICollection.IsSynchronized { get { throw null; } }
615627
object System.Collections.ICollection.SyncRoot { get { throw null; } }
@@ -701,6 +713,9 @@ internal ImmutableQueue() { }
701713
public System.Collections.Immutable.ImmutableQueue<T> Enqueue(T value) { throw null; }
702714
public System.Collections.Immutable.ImmutableQueue<T>.Enumerator GetEnumerator() { throw null; }
703715
public T Peek() { throw null; }
716+
#if FEATURE_ITEMREFAPI
717+
public ref readonly T PeekRef() { throw null; }
718+
#endif
704719
System.Collections.Generic.IEnumerator<T> System.Collections.Generic.IEnumerable<T>.GetEnumerator() { throw null; }
705720
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; }
706721
System.Collections.Immutable.IImmutableQueue<T> System.Collections.Immutable.IImmutableQueue<T>.Clear() { throw null; }
@@ -739,6 +754,9 @@ internal ImmutableSortedDictionary() { }
739754
public int Count { get { throw null; } }
740755
public bool IsEmpty { get { throw null; } }
741756
public TValue this[TKey key] { get { throw null; } }
757+
#if FEATURE_ITEMREFAPI
758+
public ref readonly TValue ValueRef(TKey key) { throw null; }
759+
#endif
742760
public System.Collections.Generic.IComparer<TKey> KeyComparer { get { throw null; } }
743761
public System.Collections.Generic.IEnumerable<TKey> Keys { get { throw null; } }
744762
bool System.Collections.Generic.ICollection<System.Collections.Generic.KeyValuePair<TKey,TValue>>.IsReadOnly { get { throw null; } }
@@ -796,6 +814,9 @@ public sealed partial class Builder : System.Collections.Generic.ICollection<Sys
796814
internal Builder() { }
797815
public int Count { get { throw null; } }
798816
public TValue this[TKey key] { get { throw null; } set { } }
817+
#if FEATURE_ITEMREFAPI
818+
public ref readonly TValue ValueRef(TKey key) { throw null; }
819+
#endif
799820
public System.Collections.Generic.IComparer<TKey> KeyComparer { get { throw null; } set { } }
800821
public System.Collections.Generic.IEnumerable<TKey> Keys { get { throw null; } }
801822
bool System.Collections.Generic.ICollection<System.Collections.Generic.KeyValuePair<TKey,TValue>>.IsReadOnly { get { throw null; } }
@@ -868,6 +889,9 @@ internal ImmutableSortedSet() { }
868889
public int Count { get { throw null; } }
869890
public bool IsEmpty { get { throw null; } }
870891
public T this[int index] { get { throw null; } }
892+
#if FEATURE_ITEMREFAPI
893+
public ref readonly T ItemRef(int index) { throw null; }
894+
#endif
871895
public System.Collections.Generic.IComparer<T> KeyComparer { get { throw null; } }
872896
public T Max { get { throw null; } }
873897
public T Min { get { throw null; } }
@@ -931,6 +955,9 @@ public sealed partial class Builder : System.Collections.Generic.ICollection<T>,
931955
internal Builder() { }
932956
public int Count { get { throw null; } }
933957
public T this[int index] { get { throw null; } }
958+
#if FEATURE_ITEMREFAPI
959+
public ref readonly T ItemRef(int index) { throw null; }
960+
#endif
934961
public System.Collections.Generic.IComparer<T> KeyComparer { get { throw null; } set { } }
935962
public T Max { get { throw null; } }
936963
public T Min { get { throw null; } }
@@ -987,6 +1014,9 @@ internal ImmutableStack() { }
9871014
public System.Collections.Immutable.ImmutableStack<T> Clear() { throw null; }
9881015
public System.Collections.Immutable.ImmutableStack<T>.Enumerator GetEnumerator() { throw null; }
9891016
public T Peek() { throw null; }
1017+
#if FEATURE_ITEMREFAPI
1018+
public ref readonly T PeekRef() { throw null; }
1019+
#endif
9901020
public System.Collections.Immutable.ImmutableStack<T> Pop() { throw null; }
9911021
public System.Collections.Immutable.ImmutableStack<T> Pop(out T value) { value = default(T); throw null; }
9921022
public System.Collections.Immutable.ImmutableStack<T> Push(T value) { throw null; }

src/System.Collections.Immutable/ref/System.Collections.Immutable.csproj

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,29 @@
1010
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard-Release|AnyCPU'" />
1111
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard1.0-Debug|AnyCPU'" />
1212
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard1.0-Release|AnyCPU'" />
13+
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard1.3-Debug|AnyCPU'" />
14+
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard1.3-Release|AnyCPU'" />
1315
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'uap-Windows_NT-Debug|AnyCPU'" />
1416
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'uap-Windows_NT-Release|AnyCPU'" />
17+
<PropertyGroup Condition="'$(TargetGroup)' != 'netstandard1.0'">
18+
<DefineConstants>$(DefineConstants);FEATURE_ITEMREFAPI</DefineConstants>
19+
</PropertyGroup>
1520
<ItemGroup>
1621
<Compile Include="System.Collections.Immutable.cs" />
1722
</ItemGroup>
1823
<ItemGroup Condition="'$(TargetGroup)' == 'netcoreapp' Or '$(TargetGroup)' == 'uap'">
1924
<ProjectReference Include="..\..\System.Collections\ref\System.Collections.csproj" />
2025
<ProjectReference Include="..\..\System.Runtime\ref\System.Runtime.csproj" />
26+
<ProjectReference Include="..\..\System.Runtime.InteropServices\ref\System.Runtime.InteropServices.csproj" />
2127
</ItemGroup>
2228
<ItemGroup Condition="'$(TargetGroup)' == 'netstandard1.0'">
2329
<Reference Include="System.Runtime" />
2430
<Reference Include="System.Collections" />
2531
</ItemGroup>
32+
<ItemGroup Condition="'$(TargetGroup)' == 'netstandard1.3'">
33+
<Reference Include="System.Runtime" />
34+
<Reference Include="System.Collections" />
35+
<Reference Include="System.Runtime.InteropServices" />
36+
</ItemGroup>
2637
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
2738
</Project>

src/System.Collections.Immutable/src/Configurations.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
<PropertyGroup>
44
<PackageConfigurations>
55
netstandard1.0;
6+
netstandard1.3;
67
netstandard;
78
</PackageConfigurations>
89
<BuildConfigurations>

src/System.Collections.Immutable/src/System.Collections.Immutable.csproj

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,20 @@
99
<FileAlignment>512</FileAlignment>
1010
<DocumentationFile>$(OutputPath)$(MSBuildProjectName).xml</DocumentationFile>
1111
<GenerateAppxPackageOnBuild>False</GenerateAppxPackageOnBuild>
12-
<PackageTargetFramework Condition="'$(TargetGroup)' == 'netstandard1.0'">netstandard1.0;portable-net45+win8+wp8+wpa81</PackageTargetFramework>
1312
</PropertyGroup>
1413
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netcoreapp-Debug|AnyCPU'" />
1514
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netcoreapp-Release|AnyCPU'" />
1615
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard-Debug|AnyCPU'" />
1716
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard-Release|AnyCPU'" />
1817
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard1.0-Debug|AnyCPU'" />
1918
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard1.0-Release|AnyCPU'" />
19+
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard1.3-Debug|AnyCPU'" />
20+
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard1.3-Release|AnyCPU'" />
2021
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'uap-Windows_NT-Debug|AnyCPU'" />
2122
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'uap-Windows_NT-Release|AnyCPU'" />
23+
<PropertyGroup Condition="'$(TargetGroup)' != 'netstandard1.0'">
24+
<DefineConstants>$(DefineConstants);FEATURE_ITEMREFAPI</DefineConstants>
25+
</PropertyGroup>
2226
<ItemGroup>
2327
<Compile Include="Properties\InternalsVisibleTo.cs" />
2428
<Compile Include="GlobalSuppressions.cs" />
@@ -101,7 +105,7 @@
101105
<Link>Common\System\Runtime\Versioning\NonVersionableAttribute.cs</Link>
102106
</Compile>
103107
</ItemGroup>
104-
<ItemGroup Condition="'$(TargetGroup)' == 'netstandard1.0'">
108+
<ItemGroup Condition="'$(TargetGroup)' == 'netstandard1.0' or '$(TargetGroup)' == 'netstandard1.3'">
105109
<Compile Include="$(CommonPath)\System\Diagnostics\CodeAnalysis\ExcludeFromCodeCoverageAttribute.cs">
106110
<Link>Common\System\Diagnostics\CodeAnalysis\ExcludeFromCodeCoverageAttribute.cs</Link>
107111
</Compile>
@@ -119,5 +123,8 @@
119123
<Reference Include="System.Runtime.Extensions" />
120124
<Reference Include="System.Threading" />
121125
</ItemGroup>
126+
<ItemGroup Condition="'$(TargetGroup)' == 'netstandard1.3' or '$(TargetGroup)' == 'netcoreapp' or '$(TargetGroup)' == 'uap'">
127+
<Reference Include="System.Runtime.InteropServices" />
128+
</ItemGroup>
122129
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
123130
</Project>

src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Builder.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,25 @@ public T this[int index]
157157
}
158158
}
159159

160+
#if FEATURE_ITEMREFAPI
161+
/// <summary>
162+
/// Gets a read-only reference to the element at the specified index.
163+
/// </summary>
164+
/// <param name="index">The index.</param>
165+
/// <returns></returns>
166+
/// <exception cref="IndexOutOfRangeException">
167+
/// </exception>
168+
public ref readonly T ItemRef(int index)
169+
{
170+
if (index >= this.Count)
171+
{
172+
throw new IndexOutOfRangeException();
173+
}
174+
175+
return ref this._elements[index];
176+
}
177+
#endif
178+
160179
/// <summary>
161180
/// Gets a value indicating whether the <see cref="ICollection{T}"/> is read-only.
162181
/// </summary>

src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Minimal.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,23 @@ public T this[int index]
129129
}
130130
}
131131

132+
#if FEATURE_ITEMREFAPI
133+
/// <summary>
134+
/// Gets a read-only reference to the element at the specified index in the read-only list.
135+
/// </summary>
136+
/// <param name="index">The zero-based index of the element to get a reference to.</param>
137+
/// <returns>A read-only reference to the element at the specified index in the read-only list.</returns>
138+
public ref readonly T ItemRef(int index)
139+
{
140+
// We intentionally do not check this.array != null, and throw NullReferenceException
141+
// if this is called while uninitialized.
142+
// The reason for this is perf.
143+
// Length and the indexer must be absolutely trivially implemented for the JIT optimization
144+
// of removing array bounds checking to work.
145+
return ref this.array[index];
146+
}
147+
#endif
148+
132149
/// <summary>
133150
/// Gets a value indicating whether this collection is empty.
134151
/// </summary>

src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.HashBucket.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,11 @@ internal HashBucket Add(TKey key, TValue value, IEqualityComparer<KeyValuePair<T
186186
result = OperationResult.NoChangeRequired;
187187
return this;
188188
case KeyCollisionBehavior.ThrowIfValueDifferent:
189+
#if FEATURE_ITEMREFAPI
190+
ref readonly var existingEntry = ref _additionalElements.ItemRef(keyCollisionIndex);
191+
#else
189192
var existingEntry = _additionalElements[keyCollisionIndex];
193+
#endif
190194
if (!valueComparer.Equals(existingEntry.Value, value))
191195
{
192196
throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, SR.DuplicateKey, key));
@@ -277,7 +281,11 @@ internal bool TryGetValue(TKey key, IEqualityComparer<KeyValuePair<TKey, TValue>
277281
return false;
278282
}
279283

284+
#if FEATURE_ITEMREFAPI
285+
value = _additionalElements.ItemRef(index).Value;
286+
#else
280287
value = _additionalElements[index].Value;
288+
#endif
281289
return true;
282290
}
283291

@@ -316,7 +324,11 @@ internal bool TryGetKey(TKey equalKey, IEqualityComparer<KeyValuePair<TKey, TVal
316324
return false;
317325
}
318326

327+
#if FEATURE_ITEMREFAPI
328+
actualKey = _additionalElements.ItemRef(index).Key;
329+
#else
319330
actualKey = _additionalElements[index].Key;
331+
#endif
320332
return true;
321333
}
322334

src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableHashSet_1.HashBucket.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,11 @@ internal bool TryExchange(T value, IEqualityComparer<T> valueComparer, out T exi
182182
int index = _additionalElements.IndexOf(value, valueComparer);
183183
if (index >= 0)
184184
{
185+
#if FEATURE_ITEMREFAPI
186+
existingValue = _additionalElements.ItemRef(index);
187+
#else
185188
existingValue = _additionalElements[index];
189+
#endif
186190
return true;
187191
}
188192
}

src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList_1.Builder.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,11 @@ public T this[int index]
135135
{
136136
get
137137
{
138+
#if FEATURE_ITEMREFAPI
139+
return this.Root.ItemRef(index);
140+
#else
138141
return this.Root[index];
142+
#endif
139143
}
140144

141145
set
@@ -155,6 +159,18 @@ T IOrderedCollection<T>.this[int index]
155159
}
156160
}
157161

162+
#if FEATURE_ITEMREFAPI
163+
/// <summary>
164+
/// Gets a read-only reference to the value for a given index into the list.
165+
/// </summary>
166+
/// <param name="index">The index of the desired element.</param>
167+
/// <returns>A read-only reference to the value at the specified index.</returns>
168+
public ref readonly T ItemRef(int index)
169+
{
170+
return ref this.Root.ItemRef(index);
171+
}
172+
#endif
173+
158174
#endregion
159175

160176
#region IList<T> Methods

0 commit comments

Comments
 (0)