|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Build.Shared;
#nullable disable
namespace Microsoft.Build.Collections
{
/// <summary>
/// A class which implements IEnumerable by creating a copy of the backing collection.
/// </summary>
/// <remarks>
/// <see cref="GetEnumerator()"/> is thread safe for concurrent access.
/// </remarks>
/// <typeparam name="TSource">The type contained in the backing collection.</typeparam>
/// <typeparam name="TResult">The type of items being enumerated.</typeparam>
internal class CopyOnReadEnumerable<TSource, TResult> : IEnumerable<TResult>
{
/// <summary>
/// The backing collection.
/// </summary>
private readonly IEnumerable<TSource> _backingEnumerable;
/// <summary>
/// The object used to synchronize access for copying.
/// </summary>
private readonly object _syncRoot;
/// <summary>
/// The function to translate items in the backing collection to the resulting type.
/// </summary>
private readonly Func<TSource, TResult> _selector;
/// <summary>
/// Constructor.
/// </summary>
/// <param name="backingEnumerable">The collection which serves as a source for enumeration.</param>
/// <param name="syncRoot">The object used to synchronize access for copying.</param>
/// <param name="selector">function to translate items in the backing collection to the resulting type.</param>
public CopyOnReadEnumerable(IEnumerable<TSource> backingEnumerable, object syncRoot, Func<TSource, TResult> selector)
{
ErrorUtilities.VerifyThrowArgumentNull(backingEnumerable);
ErrorUtilities.VerifyThrowArgumentNull(syncRoot);
_backingEnumerable = backingEnumerable;
_syncRoot = syncRoot;
_selector = selector;
}
#region IEnumerable<T> Members
/// <summary>
/// Returns an enumerator over the collection.
/// </summary>
/// <returns>The enumerator.</returns>
public IEnumerator<TResult> GetEnumerator()
{
List<TResult> list;
#if NETCOREAPP
if (_backingEnumerable.TryGetNonEnumeratedCount(out int count))
{
#else
if (_backingEnumerable is ICollection backingCollection)
{
int count = backingCollection.Count;
#endif
list = new List<TResult>(count);
}
else
{
list = new List<TResult>();
}
lock (_syncRoot)
{
list.AddRange(_backingEnumerable.Select(_selector));
}
return list.GetEnumerator();
}
#endregion
#region IEnumerable Members
/// <summary>
/// Returns an numerator over the collection.
/// </summary>
/// <returns>The enumerator.</returns>
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable<TResult>)this).GetEnumerator();
}
#endregion
}
}
|