You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

768 lines
33 KiB
C#

// This project is licensed under The MIT License (MIT)
//
// Copyright 2013 David Koontz, Logan Barnett, Corey Nolan, Alex Burley
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// Please direct questions, patches, and suggestions to the project page at
// https://github.com/dkoontz/GoodStuff
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Otter {
public delegate bool Predicate<T1, T2>(T1 item1, T2 item2);
public static class IntExtensions {
/// <summary>
/// Calls the provided callback action repeatedly.
/// </summary>
/// <description>
/// Used to invoke an action a fixed number of times.
///
/// 5.Times(() => Console.WriteLine("Hey!"));
///
/// is the equivalent of
///
/// for(var i = 0; i < 5; i++) {
/// Console.WriteLine("Hey!");
/// }
/// </description>
public static void Times(this int iterations, Action callback) {
for (var i = 0; i < iterations; ++i) {
callback();
}
}
/// <summary>
/// Calls the provided callback action repeatedly passing in the current value of i
/// </summary>
/// <description>
/// Used to invoke an action a fixed number of times.
///
/// 5.Times(i => Console.WriteLine("Hey # " + i));
///
/// is the equivalent of
///
/// for(var i = 0; i < 5; i++) {
/// Console.WriteLine("Hey # " + i);
/// }
/// </description>
public static void Times(this int iterations, Action<int> callback) {
for (var i = 0; i < iterations; ++i) {
callback(i);
}
}
/// <summary>
/// Iterates from the start up to the given end value inclusive, calling the provided callback with each value in the sequence.
/// </summary>
/// <description>
/// Used to iterate from a start value to a target value
///
/// 0.UpTo(5, i => Console.WriteLine(i));
///
/// is the equivalent of
///
/// for(var i = 0; i <= 5; i++) {
/// Console.WriteLine(i);
/// }
/// </description>
public static void UpTo(this int value, int endValue, Action<int> callback) {
for (var i = value; i <= endValue; ++i) {
callback(i);
}
}
/// <summary>
/// Iterates from the start down to the given end value inclusive, calling the provided callback with each value in the sequence.
/// </summary>
/// <description>
/// Used to iterate from a start value to a target value
///
/// 5.DownTo(0, i => Console.WriteLine(i));
///
/// is the equivalent of
///
/// for(var i = 5; i >= 0; i++) {
/// Console.WriteLine(i);
/// }
/// </description>
public static void DownTo(this int value, int endValue, Action<int> callback) {
for (var i = value; i >= endValue; --i) {
callback(i);
}
}
public static bool IsEven(this int value) {
return value % 2 == 0;
}
public static bool IsOdd(this int value) {
return value % 2 == 1;
}
}
public static class FloatExtensions {
/// <summary>
/// Maps a value in one range to the equivalent value in another range.
/// </summary>
public static float MapToRange(this float value, float range1Min, float range1Max, float range2Min, float range2Max) {
return MapToRange(value, range1Min, range1Max, range2Min, range2Max, true);
}
/// <summary>
/// Maps a value in one range to the equivalent value in another range. Clamps the value to be valid within the range if clamp is specified as true.
/// </summary>
public static float MapToRange(this float value, float range1Min, float range1Max, float range2Min, float range2Max, bool clamp) {
value = range2Min + ((value - range1Min) / (range1Max - range1Min)) * (range2Max - range2Min);
if (clamp) {
if (range2Min < range2Max) {
if (value > range2Max) value = range2Max;
if (value < range2Min) value = range2Min;
}
// Range that go negative are possible, for example from 0 to -1
else {
if (value > range2Min) value = range2Min;
if (value < range2Max) value = range2Max;
}
}
return value;
}
/// <summary>
/// Converts a float into a percent value (1 => 100%)
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static int ToPercent(this float value) {
return Convert.ToInt32(value * 100);
}
}
public static class IEnumerableExtensions {
/// <summary>
/// Iterates over each element in the IEnumerable, passing in the element to the provided callback.
/// </summary>
public static void Each<T>(this IEnumerable<T> iterable, Action<T> callback) {
foreach (var value in iterable) {
callback(value);
}
}
/// <summary>
/// Iterates over each element backwards in the IEnumerable, passing in the element to the provided callback.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="iterable"></param>
/// <param name="callback"></param>
public static void EachReverse<T>(this IEnumerable<T> iterable, Action<T> callback) {
for (var i = iterable.Count() - 1; i >= 0; i--) {
callback(iterable.ElementAt(i));
}
}
/// <summary>
/// Iterates over each element in the IEnumerable, passing in the element to the provided callback. Since the IEnumerable is
/// not generic, a type must be specified as a type parameter to Each.
/// </summary>
/// <description>
/// IEnumerable myCollection = new List<int>();
/// ...
/// myCollection.Each<int>(i => Debug.Log("i: " + i));
/// </description>
public static void Each<T>(this IEnumerable iterable, Action<T> callback) {
foreach (T value in iterable) {
callback(value);
}
}
// /// <summary>
// /// Iterates over each element in the IEnumerable, passing in the element to the provided callback.
// /// </summary>
// public static void Each(this IEnumerable iterable, Action<object> callback) {
// foreach(object value in iterable) {
// callback(value);
// }
// }
/// <summary>
/// Iterates over each element in the IEnumerable, passing in the element and the index to the provided callback.
/// </summary>
public static void EachWithIndex<T>(this IEnumerable<T> iterable, Action<T, int> callback) {
var i = 0;
foreach (var value in iterable) {
callback(value, i);
++i;
}
}
/// <summary>
/// Iterates over each element in the IEnumerable, passing in the element and the index to the provided callback.
/// </summary>
public static void EachWithIndex<T>(this IEnumerable iterable, Action<T, int> callback) {
var i = 0;
foreach (T value in iterable) {
callback(value, i);
++i;
}
}
/// <summary>
/// Iterates over each element in the two dimensional array, passing in the index to the provided callback.
/// </summary>
public static void EachIndex<T>(this IEnumerable<T> iterable, Action<int> callback) {
var i = 0;
#pragma warning disable 0168
foreach (var value in iterable) {
#pragma warning restore 0168
callback(i);
++i;
}
}
/// <summary>
/// Iterates over each element in the two dimensional array, passing in the index to the provided callback.
/// </summary>
public static void EachIndex<T>(this IEnumerable iterable, Action<int> callback) {
var i = 0;
#pragma warning disable 0219
foreach (T value in iterable) {
#pragma warning restore 0219
callback(i);
++i;
}
}
/// <summary>
/// Iterates over each element in both the iterable1 and iterable2 collections, passing in the current element of each collection into the provided callback.
/// </summary>
public static void InParallelWith<T, U>(this IEnumerable<T> iterable1, IEnumerable<U> iterable2, Action<T, U> callback) {
if (iterable1.Count() != iterable2.Count()) throw new ArgumentException(string.Format("Both IEnumerables must be the same length, iterable1: {0}, iterable2: {1}", iterable1.Count(), iterable2.Count()));
var i1Enumerator = iterable1.GetEnumerator();
var i2Enumerator = iterable2.GetEnumerator();
while (i1Enumerator.MoveNext()) {
i2Enumerator.MoveNext();
callback(i1Enumerator.Current, i2Enumerator.Current);
}
}
/// <summary>
/// Iterates over each element in both the iterable1 and iterable2 collections, passing in the current element of each collection into the provided callback.
/// </summary>
public static void InParallelWith(this IEnumerable iterable1, IEnumerable iterable2, Action<object, object> callback) {
var i1Enumerator = iterable1.GetEnumerator();
var i2Enumerator = iterable2.GetEnumerator();
var i1Count = 0;
var i2Count = 0;
while (i1Enumerator.MoveNext()) ++i1Count;
while (i2Enumerator.MoveNext()) ++i2Count;
if (i1Count != i2Count) throw new ArgumentException(string.Format("Both IEnumerables must be the same length, iterable1: {0}, iterable2: {1}", i1Count, i2Count));
i1Enumerator.Reset();
i2Enumerator.Reset();
while (i1Enumerator.MoveNext()) {
i2Enumerator.MoveNext();
callback(i1Enumerator.Current, i2Enumerator.Current);
}
}
public static bool IsEmpty<T>(this IEnumerable<T> iterable) {
return iterable.Count() == 0;
}
public static bool IsEmpty(this IEnumerable iterable) {
// MoveNext returns false if we are at the end of the collection
return !iterable.GetEnumerator().MoveNext();
}
public static bool IsNotEmpty<T>(this IEnumerable<T> iterable) {
return iterable.Count() > 0;
}
public static bool IsNotEmpty(this IEnumerable iterable) {
// MoveNext returns false if we are at the end of the collection
return iterable.GetEnumerator().MoveNext();
}
/// <summary>
/// Matches all elements where the given condition is not true. This is the
/// opposite of Linq's Where clause.
/// </summary>
public static IEnumerable<T> ExceptWhere<T>(this IEnumerable<T> iterable, Func<T, bool> condition) {
return iterable.Where(element => !condition(element));
}
#region MoreLINQ project code
// MinBy and MoreBy methods are provided via the MoreLINQ project (c) Jon Skeet
// https://code.google.com/p/morelinq/source/browse/MoreLinq/MinBy.cs
// https://code.google.com/p/morelinq/source/browse/MoreLinq/MaxBy.cs
/// <summary>
/// Returns the first element that has the smallest value (as determined by the selector) within the collection
/// (as determined by the comparer). This is equivalent to using Min except that the element itself
/// is returned, and not the value used to make the Min determination.
/// </summary>
public static TSource MinBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> selector) {
return source.MinBy(selector, Comparer<TKey>.Default);
}
/// <summary>
/// Returns the first element that has the smallest value (as determined by the selector) within the collection
/// (as determined by the comparer). This is equivalent to using Min except that the element itself
/// is returned, and not the value used to make the Min determination.
/// </summary>
public static TSource MinBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> selector, IComparer<TKey> comparer) {
if (source == null) throw new ArgumentNullException("source");
if (selector == null) throw new ArgumentNullException("selector");
if (comparer == null) throw new ArgumentNullException("comparer");
using (var sourceIterator = source.GetEnumerator()) {
if (!sourceIterator.MoveNext()) {
throw new InvalidOperationException("Sequence contains no elements");
}
var minValue = sourceIterator.Current;
var minKey = selector(minValue);
while (sourceIterator.MoveNext()) {
var candidate = sourceIterator.Current;
var candidateProjected = selector(candidate);
if (comparer.Compare(candidateProjected, minKey) < 0) {
minValue = candidate;
minKey = candidateProjected;
}
}
return minValue;
}
}
/// <summary>
/// Returns the first element that has the largest value (as determined by the selector) within the collection
/// (as determined by the comparer). This is equivalent to using Max except that the element itself
/// is returned, and not the value used to make the Max determination.
/// </summary>
public static TSource MaxBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> selector) {
return source.MaxBy(selector, Comparer<TKey>.Default);
}
/// <summary>
/// Returns the first element that has the largest value (as determined by the selector) within the collection
/// (as determined by the comparer). This is equivalent to using Max except that the element itself
/// is returned, and not the value used to make the Max determination.
/// </summary>
public static TSource MaxBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> selector, IComparer<TKey> comparer) {
if (source == null) throw new ArgumentNullException("source");
if (selector == null) throw new ArgumentNullException("selector");
if (comparer == null) throw new ArgumentNullException("comparer");
using (var sourceIterator = source.GetEnumerator()) {
if (!sourceIterator.MoveNext()) {
throw new InvalidOperationException("Sequence contains no elements");
}
var maxValue = sourceIterator.Current;
var maxKey = selector(maxValue);
while (sourceIterator.MoveNext()) {
var candidate = sourceIterator.Current;
var candidateProjected = selector(candidate);
if (comparer.Compare(candidateProjected, maxKey) > 0) {
maxValue = candidate;
maxKey = candidateProjected;
}
}
return maxValue;
}
}
#endregion
}
public static class ArrayExtensions {
[ThreadStatic]
static System.Random randomNumberGenerator = new Random(DateTime.Now.Millisecond + System.Threading.Thread.CurrentThread.GetHashCode());
/// <summary>
/// Returns the first index in the array where the target exists. If the target cannot be found, returns -1.
/// </summary>
public static int IndexOf<T>(this T[] array, T target) {
for (var i = 0; i < array.Length; ++i) {
if (array[i].Equals(target)) return i;
}
return -1;
}
/// <summary>
/// Returns a sub-section of the current array, starting at the specified index and continuing to the end of the array.
/// </summary>
public static T[] FromIndexToEnd<T>(this T[] array, int start) {
var subSection = new T[array.Length - start];
array.CopyTo(subSection, start);
return subSection;
}
/// <summary>
/// Wrapper for System.Array.FindIndex to allow it to be called directly on an array.
/// </summary>
public static int FindIndex<T>(this T[] array, Predicate<T> match) {
return Array.FindIndex(array, match);
}
/// <summary>
/// Wrapper for System.Array.FindIndex to allow it to be called directly on an array.
/// </summary>
public static int FindIndex<T>(this T[] array, int startIndex, Predicate<T> match) {
return Array.FindIndex(array, startIndex, match);
}
/// <summary>
/// Wrapper for System.Array.FindIndex to allow it to be called directly on an array.
/// </summary>
public static int FindIndex<T>(this T[] array, int startIndex, int count, Predicate<T> match) {
return Array.FindIndex(array, startIndex, count, match);
}
/// Returns a randomly selected item from the array
public static T RandomElement<T>(this T[] array) {
if (array.Length == 0) throw new IndexOutOfRangeException("Cannot retrieve a random value from an empty array");
return array[randomNumberGenerator.Next(array.Length)];
}
/// Returns a randomly selected item from the array determined by a float array of weights
public static T RandomElement<T>(this T[] array, float[] weights) {
return array.RandomElement(weights.ToList());
}
/// Returns a randomly selected item from the array determined by a List<float> of weights
public static T RandomElement<T>(this T[] array, List<float> weights) {
if (array.IsEmpty()) throw new IndexOutOfRangeException("Cannot retrieve a random value from an empty array");
if (array.Count() != weights.Count()) throw new IndexOutOfRangeException("array of weights must be the same size as input array");
var randomWeight = randomNumberGenerator.NextDouble() * weights.Sum();
var totalWeight = 0f;
var index = weights.FindIndex(weight => {
totalWeight += weight;
return randomWeight <= totalWeight;
});
return array[index];
}
/// <summary>
/// Iterates over each element in the two dimensional array, passing in the element and the index to the provided callback.
/// </summary>
public static void EachWithIndex<T>(this T[,] collection, Action<T, int, int> callback) {
for (var x = 0; x < collection.GetLength(0); ++x) {
for (var y = 0; y < collection.GetLength(1); ++y) {
callback(collection[x, y], x, y);
}
}
}
/// <summary>
/// Iterates over each element in the two dimensional array, passing in the index to the provided callback.
/// </summary>
public static void EachIndex<T>(this T[,] collection, Action<int, int> callback) {
for (var x = 0; x < collection.GetLength(0); ++x) {
for (var y = 0; y < collection.GetLength(1); ++y) {
callback(x, y);
}
}
}
}
public static class ListExtensions {
[ThreadStatic]
static System.Random randomNumberGenerator = new Random(DateTime.Now.Millisecond + System.Threading.Thread.CurrentThread.GetHashCode());
/// <summary>
/// Returns a sub-section of the current list, starting at the specified index and continuing to the end of the list.
/// </summary>
public static List<T> FromIndexToEnd<T>(this List<T> list, int start) {
return list.GetRange(start, list.Count - start);
}
/// <summary>
/// Returns the first index in the IList<T> where the target exists. If the target cannot be found, returns -1.
/// </summary>
public static int IndexOf<T>(this IList<T> list, T target) {
for (var i = 0; i < list.Count; ++i) {
if (list[i].Equals(target)) return i;
}
return -1;
}
/// Returns a randomly selected item from IList<T>
public static T RandomElement<T>(this IList<T> list) {
if (list.IsEmpty()) throw new IndexOutOfRangeException("Cannot retrieve a random value from an empty list");
return list[Rand.Int(list.Count)]; // Using Otter's RNG for consistency
//return list[randomNumberGenerator.Next(list.Count)];
}
/// <summary>
/// Returns a randomly selected item from IList or default.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="list"></param>
/// <returns></returns>
public static T RandomElementOrDefault<T>(this IList<T> list) {
if (list.IsEmpty()) return default(T);
return RandomElement(list);
}
/// Returns a randomly selected item from IList<T> determined by a IEnumerable<float> of weights
public static T RandomElement<T>(this IList<T> list, IEnumerable<float> weights) {
if (list.IsEmpty()) throw new IndexOutOfRangeException("Cannot retrieve a random value from an empty list");
if (list.Count != weights.Count()) throw new IndexOutOfRangeException("List of weights must be the same size as input list");
var randomWeight = randomNumberGenerator.NextDouble() * weights.Sum();
var totalWeight = 0f;
var index = 0;
foreach (var weight in weights) {
totalWeight += weight;
if (randomWeight <= totalWeight) {
break;
}
}
return list[index];
}
public static IList<T> Shuffle<T>(this IList<T> list) {
// OrderBy and Sort are both broken for AOT compliation on older MonoTouch versions
// https://bugzilla.xamarin.com/show_bug.cgi?id=2155#c11
var shuffledList = new List<T>(list);
T temp;
for (var i = 0; i < shuffledList.Count; ++i) {
temp = shuffledList[i];
var swapIndex = randomNumberGenerator.Next(list.Count);
shuffledList[i] = shuffledList[swapIndex];
shuffledList[swapIndex] = temp;
}
return shuffledList;
}
public static IList<T> InPlaceShuffle<T>(this IList<T> list) {
// OrderBy and Sort are both broken for AOT compliation on older MonoTouch versions
// https://bugzilla.xamarin.com/show_bug.cgi?id=2155#c11
for (var i = 0; i < list.Count; ++i) {
var temp = list[i];
var swapIndex = randomNumberGenerator.Next(list.Count);
list[i] = list[swapIndex];
list[swapIndex] = temp;
}
return list;
}
public static IList<T> InPlaceOrderBy<T, TKey>(this IList<T> list, Func<T, TKey> elementToSortValue) where TKey : IComparable {
// Provides both and in-place sort as well as an AOT on iOS friendly replacement for OrderBy
if (list.Count < 2) {
return list;
}
int startIndex;
int currentIndex;
int smallestIndex;
T temp;
for (startIndex = 0; startIndex < list.Count; ++startIndex) {
smallestIndex = startIndex;
for (currentIndex = startIndex + 1; currentIndex < list.Count; ++currentIndex) {
if (elementToSortValue(list[currentIndex]).CompareTo(elementToSortValue(list[smallestIndex])) < 0) {
smallestIndex = currentIndex;
}
}
temp = list[startIndex];
list[startIndex] = list[smallestIndex];
list[smallestIndex] = temp;
}
return list;
}
/// <summary>
/// Attempts to Insert the item, but Adds it if the index is invalid.
/// </summary>
public static void InsertOrAdd<T>(this IList<T> list, int atIndex, T item) {
if (atIndex >= 0 && atIndex < list.Count) {
list.Insert(atIndex, item);
}
else {
list.Add(item);
}
}
/// <summary>
/// Returns the element after the given element. This can wrap. If the element is the only one in the list, itself is returned.
/// </summary>
public static T ElementAfter<T>(this IList<T> list, T element, bool wrap = true) {
var targetIndex = list.IndexOf(element) + 1;
if (wrap) {
return targetIndex >= list.Count ? list[0] : list[targetIndex];
}
return list[targetIndex];
}
/// <summary>
/// Returns the element before the given element. This can wrap. If the element is the only one in the list, itself is returned.
/// </summary>
public static T ElementBefore<T>(this IList<T> list, T element, bool wrap = true) {
var targetIndex = list.IndexOf(element) - 1;
if (wrap) {
return targetIndex < 0 ? list[list.Count - 1] : list[targetIndex];
}
return list[targetIndex];
}
}
public static class DictionaryExtensions {
/// <summary>
/// Iterates over a Dictionary<T> passing in both the key and value to the provided callback.
/// </summary>
public static void Each<T1, T2>(this Dictionary<T1, T2> dictionary, Action<T1, T2> callback) {
foreach (var keyValuePair in dictionary) {
callback(keyValuePair.Key, keyValuePair.Value);
}
}
/// <summary>
/// Iterates over a Dictionary<T> passing in both the key and value to the provided callback.
/// </summary>
public static void EachWithIndex<T1, T2>(this Dictionary<T1, T2> dictionary, Action<T1, T2, int> callback) {
var i = 0;
foreach (var keyValuePair in dictionary) {
callback(keyValuePair.Key, keyValuePair.Value, i++);
}
}
public static void RemoveAll<T1, T2>(this Dictionary<T1, T2> dictionary, Predicate<T1, T2> callback) {
var keysToRemove = new List<T1>();
foreach (var keyValuePair in dictionary) {
if (callback(keyValuePair.Key, keyValuePair.Value)) {
keysToRemove.Add(keyValuePair.Key);
}
}
foreach (var key in keysToRemove) {
dictionary.Remove(key);
}
}
}
public static class StringExtensions {
/// <summary>
/// Interpolates the arguments into the string using string.Format
/// </summary>
/// <param name="formatString">The string to be interpolated into</param>
/// <param name="args">The values to be interpolated into the string </param>
public static string Interpolate(this string formatString, params object[] args) {
return string.Format(formatString, args);
}
/// <summary>
/// Alias for <see cref="Interpolate"/> for the typing averse
/// </summary>
/// <param name="formatString">The string to be interpolated into</param>
/// <param name="args">The values to be interpolated into the string </param>
public static string Fmt(this string formatString, params object[] args) {
return Interpolate(formatString, args);
}
public static T ToEnum<T>(this string enumValueName) {
return (T)Enum.Parse(typeof(T), enumValueName);
}
public static T ToEnum<T>(this string enumValueName, bool ignoreCase) {
return (T)Enum.Parse(typeof(T), enumValueName, ignoreCase);
}
public static string Last(this string value, int count) {
if (count > value.Length) throw new ArgumentOutOfRangeException(string.Format("Cannot return more characters than exist in the string (wanted {0} string contains {1}", count, value.Length));
return value.Substring(value.Length - count, count);
}
public static string SnakeCase(this string camelizedString) {
var parts = new List<string>();
var currentWord = new StringBuilder();
foreach (var c in camelizedString) {
if (char.IsUpper(c) && currentWord.Length > 0) {
parts.Add(currentWord.ToString());
currentWord = new StringBuilder();
}
currentWord.Append(char.ToLower(c));
}
if (currentWord.Length > 0) {
parts.Add(currentWord.ToString());
}
return string.Join("_", parts.ToArray());
}
public static string Capitalize(this string word) {
return word.Substring(0, 1).ToUpper() + word.Substring(1);
}
}
public static class TypeExtensions {
/// <summary>
/// Returns an array of all concrete subclasses of the provided type.
/// </summary>
public static Type[] Subclasses(this Type type) {
var typeList = new List<System.Type>();
System.AppDomain.CurrentDomain.GetAssemblies().Each(a => typeList.AddRange(a.GetTypes()));
return typeList.Where(t => t.IsSubclassOf(type) && !t.IsAbstract).ToArray();
}
/// <summary>
/// Returns an array of the provided type and all concrete subclasses of that type.
/// </summary>
public static Type[] TypeAndSubclasses(this Type type) {
var typeList = new List<System.Type>();
System.AppDomain.CurrentDomain.GetAssemblies().Each(a => typeList.AddRange(a.GetTypes()));
return typeList.Where(t => (t == type || t.IsSubclassOf(type)) && !t.IsAbstract).ToArray();
}
}
public static class EnumExtensions {
/// <summary>
/// Returns the next enum value wrapping to the first value if passed the last
/// </summary>
public static T Next<T>(this Enum enumValue) {
var values = Enum.GetValues(enumValue.GetType());
var enumerator = values.GetEnumerator();
while (enumerator.MoveNext()) {
if (enumerator.Current.Equals(enumValue)) {
if (enumerator.MoveNext()) {
return (T)enumerator.Current;
}
}
}
return default(T);
}
}
}