|
// 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 read-only live wrapper over a collection.
/// It does not prevent modification of the values themselves.
/// </summary>
/// <remarks>
/// There is a type with the same name in the BCL, but it is actually a ReadOnlyList and does not accept an ICollection>T<.
/// Thus this is an omission from the BCL.
/// </remarks>
/// <typeparam name="T">Type of element in the collection</typeparam>
internal sealed class ReadOnlyCollection<T> : ICollection<T>, ICollection
{
/// <summary>
/// Backing live enumerable.
/// May be a collection.
/// </summary>
private IEnumerable<T> _backing;
/// <summary>
/// Construct a read only wrapper around the current contents
/// of the IEnumerable, or around the backing collection if the
/// IEnumerable is in fact a collection.
/// </summary>
internal ReadOnlyCollection(IEnumerable<T> backing)
{
ErrorUtilities.VerifyThrow(backing != null, "Need backing collection");
_backing = backing;
}
/// <summary>
/// Return the number of items in the backing collection
/// </summary>
public int Count
{
get
{
return BackingCollection.Count;
}
}
/// <summary>
/// Returns true.
/// </summary>
public bool IsReadOnly
{
get { return true; }
}
/// <summary>
/// Whether collection is synchronized
/// </summary>
bool ICollection.IsSynchronized
{
get { return false; }
}
/// <summary>
/// Sync root
/// </summary>
object ICollection.SyncRoot
{
get { return this; }
}
/// <summary>
/// Get a backing ICollection.
/// </summary>
private ICollection<T> BackingCollection
{
get
{
ICollection<T> backingCollection = _backing as ICollection<T>;
if (backingCollection == null)
{
backingCollection = new List<T>(_backing);
_backing = backingCollection;
}
return backingCollection;
}
}
/// <summary>
/// Prohibited on read only collection: throws
/// </summary>
public void Add(T item)
{
ErrorUtilities.ThrowInvalidOperation("OM_NotSupportedReadOnlyCollection");
}
/// <summary>
/// Prohibited on read only collection: throws
/// </summary>
public void Clear()
{
ErrorUtilities.ThrowInvalidOperation("OM_NotSupportedReadOnlyCollection");
}
/// <summary>
/// Pass through for underlying collection
/// </summary>
public bool Contains(T item)
{
// UNDONE: IEnumerable.Contains<T>() does the ICollection check,
// so we could just use IEnumerable.Contains<T>() here.
if (!(_backing is ICollection<T>))
{
return _backing.Contains<T>(item);
}
return BackingCollection.Contains(item);
}
/// <summary>
/// Pass through for underlying collection
/// </summary>
public void CopyTo(T[] array, int arrayIndex)
{
ErrorUtilities.VerifyThrowArgumentNull(array, nameof(array));
ICollection<T> backingCollection = _backing as ICollection<T>;
if (backingCollection != null)
{
backingCollection.CopyTo(array, arrayIndex);
}
else
{
int i = arrayIndex;
foreach (T entry in _backing)
{
array[i] = entry;
i++;
}
}
}
/// <summary>
/// Prohibited on read only collection: throws
/// </summary>
public bool Remove(T item)
{
ErrorUtilities.ThrowInvalidOperation("OM_NotSupportedReadOnlyCollection");
return false;
}
/// <summary>
/// Pass through for underlying collection
/// </summary>
/// <comment>
/// NOTE: This does NOT cause a copy into a List, since the
/// backing enumerable suffices.
/// </comment>
public IEnumerator<T> GetEnumerator()
{
return _backing.GetEnumerator();
}
/// <summary>
/// Pass through for underlying collection
/// </summary>
/// <comment>
/// NOTE: This does NOT cause a copy into a List, since the
/// backing enumerable suffices.
/// </comment>
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)_backing).GetEnumerator();
}
/// <summary>
/// ICollection version of CopyTo
/// </summary>
void ICollection.CopyTo(Array array, int index)
{
ErrorUtilities.VerifyThrowArgumentNull(array, nameof(array));
int i = index;
foreach (T entry in _backing)
{
array.SetValue(entry, i);
i++;
}
}
}
}
|