| File: Collections\UnionCollection.cs | Web Access |
| Project: src\roslyn\src\Compilers\Core\Portable\Microsoft.CodeAnalysis.csproj (Microsoft.CodeAnalysis) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Linq; using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis { /// <summary> /// Implements a readonly collection over a set of existing collections. This can be used to /// prevent having to copy items from one collection over to another (thus bloating space). /// /// Note: this is a *collection*, not a *set*. There is no removal of duplicated elements. This /// allows us to be able to efficiently do operations like CopyTo, Count, etc. in O(c) time /// instead of O(n) (where 'c' is the number of collections and 'n' is the number of elements). /// If you have a few collections with many elements in them, then this is an appropriate /// collection for you. /// </summary> internal class UnionCollection<T> : ICollection<T> { private readonly ImmutableArray<ICollection<T>> _collections; private int _count = -1; public static ICollection<T> Create(ICollection<T> coll1, ICollection<T> coll2) { Debug.Assert(coll1.IsReadOnly && coll2.IsReadOnly); // Often, one of the collections is empty. Avoid allocations in those cases. if (coll1.Count == 0) { return coll2; } if (coll2.Count == 0) { return coll1; } return new UnionCollection<T>(ImmutableArray.Create(coll1, coll2)); } public static ICollection<T> Create<TOrig>(ImmutableArray<TOrig> collections, Func<TOrig, ICollection<T>> selector) { Debug.Assert(collections.All(c => selector(c).IsReadOnly)); switch (collections.Length) { case 0: return SpecializedCollections.EmptyCollection<T>(); case 1: return selector(collections[0]); default: return new UnionCollection<T>(ImmutableArray.CreateRange(collections, selector)); } } private UnionCollection(ImmutableArray<ICollection<T>> collections) { Debug.Assert(!collections.IsDefault); _collections = collections; } public void Add(T item) { throw new NotSupportedException(); } public void Clear() { throw new NotSupportedException(); } public bool Contains(T item) { // PERF: Expansion of "return collections.Any(c => c.Contains(item));" // to avoid allocating a lambda. foreach (var c in _collections) { if (c.Contains(item)) { return true; } } return false; } public void CopyTo(T[] array, int arrayIndex) { var index = arrayIndex; foreach (var collection in _collections) { collection.CopyTo(array, index); index += collection.Count; } } public int Count { get { if (_count == -1) { _count = _collections.Sum(c => c.Count); } return _count; } } public bool IsReadOnly { get { return true; } } public bool Remove(T item) { throw new NotSupportedException(); } public IEnumerator<T> GetEnumerator() { return _collections.SelectMany(c => c).GetEnumerator(); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } } }