Skip to content

Commit dccca23

Browse files
authored
Add struct enumerator for CopyOnWritePropertyDictionary (#11806)
Fixes # ### Context ### Changes Made ### Testing ### Notes
2 parents 8bb77e7 + 71b0386 commit dccca23

File tree

1 file changed

+59
-4
lines changed

1 file changed

+59
-4
lines changed

src/Build/Collections/CopyOnWritePropertyDictionary.cs

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -133,16 +133,23 @@ public void Clear()
133133
_backing = _backing.Clear();
134134
}
135135

136+
/// <summary>
137+
/// Gets an enumerator over all the properties in the collection
138+
/// Enumeration is in undefined order. This overload exposes the struct enumerator
139+
/// directly to avoid an allocation due to boxing.
140+
/// </summary>
141+
public ImmutableDictionary<string, T>.Enumerator GetEnumerator() => _backing.GetEnumerator();
142+
136143
/// <summary>
137144
/// Gets an enumerator over all the properties in the collection
138145
/// Enumeration is in undefined order
139146
/// </summary>
140-
public IEnumerator<T> GetEnumerator() => _backing.Values.GetEnumerator();
147+
IEnumerator<T> IEnumerable<T>.GetEnumerator() => new Enumerator(this);
141148

142149
/// <summary>
143150
/// Get an enumerator over entries
144151
/// </summary>
145-
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
152+
IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this);
146153

147154
#region IEquatable<CopyOnWritePropertyDictionary<T>> Members
148155

@@ -347,9 +354,24 @@ public void Set(T projectProperty)
347354
/// <param name="other">An enumerator over the properties to add.</param>
348355
public void ImportProperties(IEnumerable<T> other)
349356
{
350-
_backing = _backing.SetItems(Items());
357+
if (other is CopyOnWritePropertyDictionary<T> copyOnWriteDictionary)
358+
{
359+
_backing = _backing.SetItems(DictionaryItems(copyOnWriteDictionary));
360+
}
361+
else
362+
{
363+
_backing = _backing.SetItems(Items(other));
364+
}
351365

352-
IEnumerable<KeyValuePair<string, T>> Items()
366+
static IEnumerable<KeyValuePair<string, T>> DictionaryItems(CopyOnWritePropertyDictionary<T> copyOnWriteDictionary)
367+
{
368+
foreach (KeyValuePair<string, T> kvp in copyOnWriteDictionary)
369+
{
370+
yield return new(kvp.Value.Key, kvp.Value);
371+
}
372+
}
373+
374+
static IEnumerable<KeyValuePair<string, T>> Items(IEnumerable<T> other)
353375
{
354376
foreach (T property in other)
355377
{
@@ -366,5 +388,38 @@ public ICopyOnWritePropertyDictionary<T> DeepClone()
366388
{
367389
return new CopyOnWritePropertyDictionary<T>(this);
368390
}
391+
392+
/// <summary>
393+
/// Struct based enumerator to expose the values of the backing collection.
394+
/// This avoids the allocation when accessing the Values property directly.
395+
/// </summary>
396+
public struct Enumerator : IEnumerator<T>
397+
{
398+
private ImmutableDictionary<string, T>.Enumerator _dictionaryEnumerator;
399+
public Enumerator(CopyOnWritePropertyDictionary<T> dictionary)
400+
{
401+
_dictionaryEnumerator = dictionary._backing.GetEnumerator();
402+
}
403+
404+
public T Current { get; private set; }
405+
406+
readonly object IEnumerator.Current => Current;
407+
408+
public void Dispose() => _dictionaryEnumerator.Dispose();
409+
410+
public bool MoveNext()
411+
{
412+
if (_dictionaryEnumerator.MoveNext())
413+
{
414+
Current = _dictionaryEnumerator.Current.Value;
415+
416+
return true;
417+
}
418+
419+
return false;
420+
}
421+
422+
public void Reset() => _dictionaryEnumerator.Reset();
423+
}
369424
}
370425
}

0 commit comments

Comments
 (0)