mirror of https://github.com/icsharpcode/ILSpy.git
				
				
			
			You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							413 lines
						
					
					
						
							11 KiB
						
					
					
				
			
		
		
	
	
							413 lines
						
					
					
						
							11 KiB
						
					
					
				#nullable enable | 
						|
using System; | 
						|
using System.Collections.Generic; | 
						|
using System.Collections.Immutable; | 
						|
using System.Linq; | 
						|
 | 
						|
namespace ICSharpCode.Decompiler.Util | 
						|
{ | 
						|
	static class CollectionExtensions | 
						|
	{ | 
						|
		public static void Deconstruct<K, V>(this KeyValuePair<K, V> pair, out K key, out V value) | 
						|
		{ | 
						|
			key = pair.Key; | 
						|
			value = pair.Value; | 
						|
		} | 
						|
 | 
						|
#if !NETCORE | 
						|
		public static IEnumerable<(A, B)> Zip<A, B>(this IEnumerable<A> input1, IEnumerable<B> input2) | 
						|
		{ | 
						|
			return input1.Zip(input2, (a, b) => (a, b)); | 
						|
		} | 
						|
#endif | 
						|
 | 
						|
		public static IEnumerable<(A?, B?)> ZipLongest<A, B>(this IEnumerable<A> input1, IEnumerable<B> input2) | 
						|
		{ | 
						|
			using (var it1 = input1.GetEnumerator()) | 
						|
			{ | 
						|
				using (var it2 = input2.GetEnumerator()) | 
						|
				{ | 
						|
					bool hasElements1 = true; | 
						|
					bool hasElements2 = true; | 
						|
					while (true) | 
						|
					{ | 
						|
						if (hasElements1) | 
						|
							hasElements1 = it1.MoveNext(); | 
						|
						if (hasElements2) | 
						|
							hasElements2 = it2.MoveNext(); | 
						|
						if (!(hasElements1 || hasElements2)) | 
						|
							break; | 
						|
						yield return ((hasElements1 ? it1.Current : default), (hasElements2 ? it2.Current : default)); | 
						|
					} | 
						|
				} | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		public static IEnumerable<T> Slice<T>(this IReadOnlyList<T> input, int offset, int length) | 
						|
		{ | 
						|
			for (int i = offset; i < offset + length; i++) | 
						|
			{ | 
						|
				yield return input[i]; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		public static IEnumerable<T> Slice<T>(this IReadOnlyList<T> input, int offset) | 
						|
		{ | 
						|
			int length = input.Count; | 
						|
			for (int i = offset; i < length; i++) | 
						|
			{ | 
						|
				yield return input[i]; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
#if !NETCORE | 
						|
		public static HashSet<T> ToHashSet<T>(this IEnumerable<T> input) | 
						|
		{ | 
						|
			return new HashSet<T>(input); | 
						|
		} | 
						|
#endif | 
						|
 | 
						|
		public static IEnumerable<T> SkipLast<T>(this IReadOnlyCollection<T> input, int count) | 
						|
		{ | 
						|
			return input.Take(input.Count - count); | 
						|
		} | 
						|
 | 
						|
		public static IEnumerable<T> TakeLast<T>(this IReadOnlyCollection<T> input, int count) | 
						|
		{ | 
						|
			return input.Skip(input.Count - count); | 
						|
		} | 
						|
 | 
						|
		public static T? PopOrDefault<T>(this Stack<T> stack) | 
						|
		{ | 
						|
			if (stack.Count == 0) | 
						|
				return default(T); | 
						|
			return stack.Pop(); | 
						|
		} | 
						|
 | 
						|
		public static T? PeekOrDefault<T>(this Stack<T> stack) | 
						|
		{ | 
						|
			if (stack.Count == 0) | 
						|
				return default(T); | 
						|
			return stack.Peek(); | 
						|
		} | 
						|
 | 
						|
		public static int MaxOrDefault<T>(this IEnumerable<T> input, Func<T, int> selector, int defaultValue = 0) | 
						|
		{ | 
						|
			int max = defaultValue; | 
						|
			foreach (var element in input) | 
						|
			{ | 
						|
				int value = selector(element); | 
						|
				if (value > max) | 
						|
					max = value; | 
						|
			} | 
						|
			return max; | 
						|
		} | 
						|
 | 
						|
		public static int IndexOf<T>(this IReadOnlyList<T> collection, T value) | 
						|
		{ | 
						|
			var comparer = EqualityComparer<T>.Default; | 
						|
			int index = 0; | 
						|
			foreach (T item in collection) | 
						|
			{ | 
						|
				if (comparer.Equals(item, value)) | 
						|
				{ | 
						|
					return index; | 
						|
				} | 
						|
				index++; | 
						|
			} | 
						|
			return -1; | 
						|
		} | 
						|
 | 
						|
		public static void AddRange<T>(this ICollection<T> collection, IEnumerable<T> input) | 
						|
		{ | 
						|
			foreach (T item in input) | 
						|
				collection.Add(item); | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Equivalent to <code>collection.Select(func).ToArray()</code>, but more efficient as it makes | 
						|
		/// use of the input collection's known size. | 
						|
		/// </summary> | 
						|
		public static U[] SelectArray<T, U>(this ICollection<T> collection, Func<T, U> func) | 
						|
		{ | 
						|
			U[] result = new U[collection.Count]; | 
						|
			int index = 0; | 
						|
			foreach (var element in collection) | 
						|
			{ | 
						|
				result[index++] = func(element); | 
						|
			} | 
						|
			return result; | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Equivalent to <code>collection.Select(func).ToImmutableArray()</code>, but more efficient as it makes | 
						|
		/// use of the input collection's known size. | 
						|
		/// </summary> | 
						|
		public static ImmutableArray<U> SelectImmutableArray<T, U>(this IReadOnlyCollection<T> collection, Func<T, U> func) | 
						|
		{ | 
						|
			var builder = ImmutableArray.CreateBuilder<U>(collection.Count); | 
						|
			foreach (var element in collection) | 
						|
			{ | 
						|
				builder.Add(func(element)); | 
						|
			} | 
						|
			return builder.MoveToImmutable(); | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Equivalent to <code>collection.Select(func).ToArray()</code>, but more efficient as it makes | 
						|
		/// use of the input collection's known size. | 
						|
		/// </summary> | 
						|
		public static U[] SelectReadOnlyArray<T, U>(this IReadOnlyCollection<T> collection, Func<T, U> func) | 
						|
		{ | 
						|
			U[] result = new U[collection.Count]; | 
						|
			int index = 0; | 
						|
			foreach (var element in collection) | 
						|
			{ | 
						|
				result[index++] = func(element); | 
						|
			} | 
						|
			return result; | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Equivalent to <code>collection.Select(func).ToArray()</code>, but more efficient as it makes | 
						|
		/// use of the input collection's known size. | 
						|
		/// </summary> | 
						|
		public static U[] SelectArray<T, U>(this List<T> collection, Func<T, U> func) | 
						|
		{ | 
						|
			U[] result = new U[collection.Count]; | 
						|
			int index = 0; | 
						|
			foreach (var element in collection) | 
						|
			{ | 
						|
				result[index++] = func(element); | 
						|
			} | 
						|
			return result; | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Equivalent to <code>collection.Select(func).ToArray()</code>, but more efficient as it makes | 
						|
		/// use of the input collection's known size. | 
						|
		/// </summary> | 
						|
		public static U[] SelectArray<T, U>(this T[] collection, Func<T, U> func) | 
						|
		{ | 
						|
			U[] result = new U[collection.Length]; | 
						|
			int index = 0; | 
						|
			foreach (var element in collection) | 
						|
			{ | 
						|
				result[index++] = func(element); | 
						|
			} | 
						|
			return result; | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Equivalent to <code>collection.Select(func).ToList()</code>, but more efficient as it makes | 
						|
		/// use of the input collection's known size. | 
						|
		/// </summary> | 
						|
		public static List<U> SelectList<T, U>(this ICollection<T> collection, Func<T, U> func) | 
						|
		{ | 
						|
			List<U> result = new List<U>(collection.Count); | 
						|
			foreach (var element in collection) | 
						|
			{ | 
						|
				result.Add(func(element)); | 
						|
			} | 
						|
			return result; | 
						|
		} | 
						|
 | 
						|
		public static IEnumerable<U> SelectWithIndex<T, U>(this IEnumerable<T> source, Func<int, T, U> func) | 
						|
		{ | 
						|
			int index = 0; | 
						|
			foreach (var element in source) | 
						|
				yield return func(index++, element); | 
						|
		} | 
						|
 | 
						|
		public static IEnumerable<(int, T)> WithIndex<T>(this ICollection<T> source) | 
						|
		{ | 
						|
			int index = 0; | 
						|
			foreach (var item in source) | 
						|
			{ | 
						|
				yield return (index, item); | 
						|
				index++; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// The merge step of merge sort. | 
						|
		/// </summary> | 
						|
		public static IEnumerable<T> Merge<T>(this IEnumerable<T> input1, IEnumerable<T> input2, Comparison<T> comparison) | 
						|
		{ | 
						|
			using (var enumA = input1.GetEnumerator()) | 
						|
			using (var enumB = input2.GetEnumerator()) | 
						|
			{ | 
						|
				bool moreA = enumA.MoveNext(); | 
						|
				bool moreB = enumB.MoveNext(); | 
						|
				while (moreA && moreB) | 
						|
				{ | 
						|
					if (comparison(enumA.Current, enumB.Current) <= 0) | 
						|
					{ | 
						|
						yield return enumA.Current; | 
						|
						moreA = enumA.MoveNext(); | 
						|
					} | 
						|
					else | 
						|
					{ | 
						|
						yield return enumB.Current; | 
						|
						moreB = enumB.MoveNext(); | 
						|
					} | 
						|
				} | 
						|
				while (moreA) | 
						|
				{ | 
						|
					yield return enumA.Current; | 
						|
					moreA = enumA.MoveNext(); | 
						|
				} | 
						|
				while (moreB) | 
						|
				{ | 
						|
					yield return enumB.Current; | 
						|
					moreB = enumB.MoveNext(); | 
						|
				} | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Returns the minimum element. | 
						|
		/// </summary> | 
						|
		/// <exception cref="InvalidOperationException">The input sequence is empty</exception> | 
						|
		public static T MinBy<T, K>(this IEnumerable<T> source, Func<T, K> keySelector) where K : IComparable<K> | 
						|
		{ | 
						|
			return source.MinBy(keySelector, Comparer<K>.Default); | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Returns the minimum element. | 
						|
		/// </summary> | 
						|
		/// <exception cref="InvalidOperationException">The input sequence is empty</exception> | 
						|
		public static T MinBy<T, K>(this IEnumerable<T> source, Func<T, K> keySelector, IComparer<K>? keyComparer) | 
						|
		{ | 
						|
			if (source == null) | 
						|
				throw new ArgumentNullException(nameof(source)); | 
						|
			if (keySelector == null) | 
						|
				throw new ArgumentNullException(nameof(keySelector)); | 
						|
			if (keyComparer == null) | 
						|
				keyComparer = Comparer<K>.Default; | 
						|
			using (var enumerator = source.GetEnumerator()) | 
						|
			{ | 
						|
				if (!enumerator.MoveNext()) | 
						|
					throw new InvalidOperationException("Sequence contains no elements"); | 
						|
				T minElement = enumerator.Current; | 
						|
				K minKey = keySelector(minElement); | 
						|
				while (enumerator.MoveNext()) | 
						|
				{ | 
						|
					T element = enumerator.Current; | 
						|
					K key = keySelector(element); | 
						|
					if (keyComparer.Compare(key, minKey) < 0) | 
						|
					{ | 
						|
						minElement = element; | 
						|
						minKey = key; | 
						|
					} | 
						|
				} | 
						|
				return minElement; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Returns the maximum element. | 
						|
		/// </summary> | 
						|
		/// <exception cref="InvalidOperationException">The input sequence is empty</exception> | 
						|
		public static T MaxBy<T, K>(this IEnumerable<T> source, Func<T, K> keySelector) where K : IComparable<K> | 
						|
		{ | 
						|
			return source.MaxBy(keySelector, Comparer<K>.Default); | 
						|
		} | 
						|
 | 
						|
		/// <summary> | 
						|
		/// Returns the maximum element. | 
						|
		/// </summary> | 
						|
		/// <exception cref="InvalidOperationException">The input sequence is empty</exception> | 
						|
		public static T MaxBy<T, K>(this IEnumerable<T> source, Func<T, K> keySelector, IComparer<K>? keyComparer) | 
						|
		{ | 
						|
			if (source == null) | 
						|
				throw new ArgumentNullException(nameof(source)); | 
						|
			if (keySelector == null) | 
						|
				throw new ArgumentNullException(nameof(keySelector)); | 
						|
			if (keyComparer == null) | 
						|
				keyComparer = Comparer<K>.Default; | 
						|
			using (var enumerator = source.GetEnumerator()) | 
						|
			{ | 
						|
				if (!enumerator.MoveNext()) | 
						|
					throw new InvalidOperationException("Sequence contains no elements"); | 
						|
				T maxElement = enumerator.Current; | 
						|
				K maxKey = keySelector(maxElement); | 
						|
				while (enumerator.MoveNext()) | 
						|
				{ | 
						|
					T element = enumerator.Current; | 
						|
					K key = keySelector(element); | 
						|
					if (keyComparer.Compare(key, maxKey) > 0) | 
						|
					{ | 
						|
						maxElement = element; | 
						|
						maxKey = key; | 
						|
					} | 
						|
				} | 
						|
				return maxElement; | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		public static void RemoveLast<T>(this IList<T> list) | 
						|
		{ | 
						|
			if (list == null) | 
						|
				throw new ArgumentNullException(nameof(list)); | 
						|
			list.RemoveAt(list.Count - 1); | 
						|
		} | 
						|
 | 
						|
		public static T? OnlyOrDefault<T>(this IEnumerable<T> source, Func<T, bool> predicate) => OnlyOrDefault(source.Where(predicate)); | 
						|
 | 
						|
		public static T? OnlyOrDefault<T>(this IEnumerable<T> source) | 
						|
		{ | 
						|
			bool any = false; | 
						|
			T? first = default; | 
						|
			foreach (var t in source) | 
						|
			{ | 
						|
				if (any) | 
						|
					return default(T); | 
						|
				first = t; | 
						|
				any = true; | 
						|
			} | 
						|
 | 
						|
			return first; | 
						|
		} | 
						|
 | 
						|
#if !NETCORE | 
						|
		public static int EnsureCapacity<T>(this List<T> list, int capacity) | 
						|
		{ | 
						|
			if (capacity < 0) | 
						|
				throw new ArgumentOutOfRangeException(nameof(capacity)); | 
						|
			if (list.Capacity < capacity) | 
						|
			{ | 
						|
				const int DefaultCapacity = 4; | 
						|
				const int MaxLength = 0X7FFFFFC7; | 
						|
 | 
						|
				int newcapacity = list.Capacity == 0 ? DefaultCapacity : 2 * list.Capacity; | 
						|
 | 
						|
				if ((uint)newcapacity > MaxLength) | 
						|
					newcapacity = MaxLength; | 
						|
 | 
						|
				if (newcapacity < capacity) | 
						|
					newcapacity = capacity; | 
						|
 | 
						|
				list.Capacity = newcapacity; | 
						|
			} | 
						|
 | 
						|
			return list.Capacity; | 
						|
		} | 
						|
#endif | 
						|
 | 
						|
		#region Aliases/shortcuts for Enumerable extension methods | 
						|
		public static bool Any<T>(this ICollection<T> list) => list.Count > 0; | 
						|
		public static bool Any<T>(this T[] array, Predicate<T> match) => Array.Exists(array, match); | 
						|
		public static bool Any<T>(this List<T> list, Predicate<T> match) => list.Exists(match); | 
						|
 | 
						|
		public static bool All<T>(this T[] array, Predicate<T> match) => Array.TrueForAll(array, match); | 
						|
		public static bool All<T>(this List<T> list, Predicate<T> match) => list.TrueForAll(match); | 
						|
 | 
						|
		public static T? FirstOrDefault<T>(this T[] array, Predicate<T> predicate) => Array.Find(array, predicate); | 
						|
		public static T? FirstOrDefault<T>(this List<T> list, Predicate<T> predicate) => list.Find(predicate); | 
						|
 | 
						|
		public static T Last<T>(this IList<T> list) => list[list.Count - 1]; | 
						|
		#endregion | 
						|
	} | 
						|
}
 | 
						|
 |