diff --git a/Source/Euonia.Core/Extensions/Extensions.Collection.cs b/Source/Euonia.Core/Extensions/Extensions.Collection.cs index f5e21c1..317c05c 100644 --- a/Source/Euonia.Core/Extensions/Extensions.Collection.cs +++ b/Source/Euonia.Core/Extensions/Extensions.Collection.cs @@ -5,1078 +5,1072 @@ public static partial class Extensions { - private static readonly Random _random = new(); - - /// - /// Performs the specified action on each element of the - /// - /// The type of the element. - /// The source. - /// The System.Action`1 delegate to perform on each element of the . - /// Throws if is null. - /// Throws if action is null. - public static void ForEach(this IEnumerable source, Action action) - { - if (source == null) - { - throw new NullReferenceException(); - } - - if (action == null) - { - throw new ArgumentNullException(nameof(action)); - } - - foreach (var value in source) - { - action(value); - } - } - - /// - /// Determines whether the collection contains the object. - /// - /// The source. - /// The value. - /// The comparison. - /// true if [contains] [the specified value]; otherwise, false. - /// - public static bool Contains(this IEnumerable source, string value, StringComparison comparison) - { - if (source == null) - { - throw new NullReferenceException(); - } - - return source.Any(t => t.Equals(value, comparison)); - } - - /// - /// Determines whether the specified collection [is null or empty]. - /// - /// The source. - /// true if the specified source [is null or empty]; otherwise, false. - public static bool IsNullOrEmpty(this IEnumerable source) - { - if (source == null) - { - return true; - } - - return !source.GetEnumerator().MoveNext(); - } - - /// - /// Determines whether a sequence is null or empty. - /// - /// - /// A sequence in which to locate a value. - /// - public static bool IsNullOrEmpty(this IEnumerable source) - { - return source == null || !source.Any(); - } - - /// - /// Determine whether the specified collection is equals to another. - /// - /// The type of the collection item. - /// The source. - /// The dest. - /// true if collection is equals to another, false otherwise. - /// - /// dest - public static bool Equals(this IEnumerable source, IEnumerable dest) where T : IComparable - { - if (source == null) - { - throw new NullReferenceException(); - } - - if (dest == null) - { - throw new ArgumentNullException(nameof(dest)); - } - - return dest.Count() == source.Count() && source.All(dest.Contains); - } - - /// - /// Concatenates the elements of an object array, using the specified separator between each element. - /// - /// Member type. - /// A collection that contains the objects to concatenate. - /// - /// - public static string Join(this IEnumerable values, string separator) - { - if (values == null) - { - throw new NullReferenceException(); - } - - return string.Join(separator, values); - } - - /// - /// Concatenates the members of a collection, using the specified separator between each member. - /// - /// Member type. - /// A collection that contains the objects to concatenate. - /// The string to use as a separator.separator is included in the returned string only if values has more than one element. - /// - /// - /// - public static string Join(this IEnumerable values, string separator, int startIndex, int count) - { - if (values == null) - { - throw new NullReferenceException(); - } - - if (startIndex >= values.Count()) - { - throw new IndexOutOfRangeException(); - } - - return values.Skip(startIndex).Take(count).Join(separator); - } - - /// - /// Convert Pageable collection to view collection. - /// - /// - /// - /// - public static ViewCollection ToView(this PageableCollection source) where T : class, new() - { - if (source == null) - { - throw new ArgumentNullException(nameof(source)); - } - - return new ViewCollection(source.ToArray(), source.TotalCount); - } - - /// - /// Read to a pageable collection. - /// - /// The type of the element. - /// The source. - /// The total count. - /// The index. - /// The size. - /// A new pageable collection contains all elements of . - /// Throws if source is null. - public static PageableCollection Paginate(this IList source, long totalCount, int index, int size) - { - if (source == null) - { - throw new NullReferenceException(); - } - - return new PageableCollection(source) { TotalCount = totalCount, PageNumber = index, PageSize = size }; - } - - /// - /// Converts an exists pageable collection to another. - /// - /// The type of collection items. - /// The source. - /// The index. - /// The size. - /// A new pageable collection. - /// Throw if source is null. - public static PageableCollection Convert(this PageableCollection source, int index, int size) - { - if (source == null) - { - throw new ArgumentNullException(nameof(source)); - } - - return new PageableCollection(source) { TotalCount = source.TotalCount, PageNumber = index, PageSize = size }; - } - - /// - /// - /// - /// - /// - /// - public static IEnumerable Shuffle(this IEnumerable enumerable) - { - var buffer = enumerable.ToList(); - - for (var i = 0; i < buffer.Count; i++) - { - var j = _random.Next(i, buffer.Count); - - yield return buffer[j]; - - buffer[j] = buffer[i]; - } - } - - /// - /// - /// - /// - /// - /// - public static ObservableCollection ToObservable(this IEnumerable source) - { - var collection = new ObservableCollection(source); - return collection; - } - - /// - /// - /// - /// - /// - /// - /// - public static IReadOnlyCollection Reify(this IEnumerable source) - { - return source switch - { - null => throw new ArgumentNullException(nameof(source)), - IReadOnlyCollection result => result, - ICollection collection => new CollectionWrapper(collection), - ICollection nonGenericCollection => new NonGenericCollectionWrapper(nonGenericCollection), - _ => new List(source) - }; - } - - /// - /// - /// - /// - /// - /// - /// - public static void InsertRange(this IList source, int index, IEnumerable items) - { - foreach (var item in items) - { - source.Insert(index++, item); - } - } - - /// - /// - /// - /// - /// - /// - /// - public static int FindIndex(this IList source, Predicate selector) - { - for (var i = 0; i < source.Count; ++i) - { - if (selector(source[i])) - { - return i; - } - } - - return -1; - } - - /// - /// - /// - /// - /// - /// - public static void AddFirst(this IList source, T item) - { - source.Insert(0, item); - } - - /// - /// - /// - /// - /// - /// - public static void AddLast(this IList source, T item) - { - source.Insert(source.Count, item); - } - - /// - /// - /// - /// - /// - /// - /// - public static void InsertAfter(this IList source, T existingItem, T item) - { - var index = source.IndexOf(existingItem); - if (index < 0) - { - source.AddFirst(item); - return; - } - - source.Insert(index + 1, item); - } - - /// - /// - /// - /// - /// - /// - /// - public static void InsertAfter(this IList source, Predicate selector, T item) - { - var index = source.FindIndex(selector); - if (index < 0) - { - source.AddFirst(item); - return; - } - - source.Insert(index + 1, item); - } - - /// - /// - /// - /// - /// - /// - /// - public static void InsertBefore(this IList source, T existingItem, T item) - { - var index = source.IndexOf(existingItem); - if (index < 0) - { - source.AddLast(item); - return; - } - - source.Insert(index, item); - } - - /// - /// - /// - /// - /// - /// - /// - public static void InsertBefore(this IList source, Predicate selector, T item) - { - var index = source.FindIndex(selector); - if (index < 0) - { - source.AddLast(item); - return; - } - - source.Insert(index, item); - } - - /// - /// - /// - /// - /// - /// - /// - public static void ReplaceWhile(this IList source, Predicate selector, T item) - { - for (var i = 0; i < source.Count; i++) - { - if (selector(source[i])) - { - source[i] = item; - } - } - } - - /// - /// - /// - /// - /// - /// - /// - public static void ReplaceWhile(this IList source, Predicate selector, Func itemFactory) - { - for (var i = 0; i < source.Count; i++) - { - var item = source[i]; - if (selector(item)) - { - source[i] = itemFactory(item); - } - } - } - - /// - /// - /// - /// - /// - /// - /// - public static void ReplaceOne(this IList source, Predicate selector, T item) - { - for (var i = 0; i < source.Count; i++) - { - if (selector(source[i])) - { - source[i] = item; - return; - } - } - } - - /// - /// - /// - /// - /// - /// - /// - public static void ReplaceOne(this IList source, Predicate selector, Func itemFactory) - { - for (var i = 0; i < source.Count; i++) - { - var item = source[i]; - if (!selector(item)) - { - continue; - } - - source[i] = itemFactory(item); - return; - } - } - - /// - /// - /// - /// - /// - /// - /// - public static void ReplaceOne(this IList source, T item, T replaceWith) - { - for (var i = 0; i < source.Count; i++) - { - if (Comparer.Default.Compare(source[i], item) != 0) - { - continue; - } - - source[i] = replaceWith; - return; - } - } - - /// - /// - /// - /// - /// - /// - /// - /// - public static void MoveItem(this List source, Predicate selector, int targetIndex) - { - if (!targetIndex.IsBetween(0, source.Count - 1)) - { - throw new IndexOutOfRangeException("targetIndex should be between 0 and " + (source.Count - 1)); - } - - var currentIndex = source.FindIndex(0, selector); - if (currentIndex == targetIndex) - { - return; - } - - var item = source[currentIndex]; - source.RemoveAt(currentIndex); - source.Insert(targetIndex, item); - } - - /// - /// - /// - /// - /// - /// - /// - /// - public static T GetOrAdd([NotNull] this IList source, Func selector, Func factory) - { - Check.EnsureNotNull(source, nameof(source)); - - var item = source.FirstOrDefault(selector); - - if (item == null) - { - item = factory(); - source.Add(item); - } - - return item; - } - - /// - /// Sort a list by a topological sorting, which consider their dependencies. - /// - /// The type of the members of values. - /// A list of objects to sort - /// Function to resolve the dependencies - /// Equality comparer for dependencies - /// - /// Returns a new list ordered by dependencies. - /// If A depends on B, then B will come before than A in the resulting list. - /// - public static List SortByDependencies( - this IEnumerable source, - Func> getDependencies, - IEqualityComparer comparer = null) - { - /* See: http://www.codeproject.com/Articles/869059/Topological-sorting-in-Csharp - * http://en.wikipedia.org/wiki/Topological_sorting - */ - - var sorted = new List(); - var visited = new Dictionary(comparer); - - foreach (var item in source) - { - SortByDependenciesVisit(item, getDependencies, sorted, visited); - } - - return sorted; - } - - /// - /// - /// - /// The type of the members of values. - /// Item to resolve - /// Function to resolve the dependencies - /// List with the sorted items - /// Dictionary with the visited items - private static void SortByDependenciesVisit(T item, Func> getDependencies, IList sorted, Dictionary visited) - { - var alreadyVisited = visited.TryGetValue(item, out var inProcess); - - if (alreadyVisited) - { - if (inProcess) - { - throw new ArgumentException("Cyclic dependency found! Item: " + item); - } - } - else - { - visited[item] = true; - - var dependencies = getDependencies(item); - if (dependencies != null) - { - foreach (var dependency in dependencies) - { - SortByDependenciesVisit(dependency, getDependencies, sorted, visited); - } - } - - visited[item] = false; - sorted.Add(item); - } - } - - /// - /// Concatenates the members of a constructed collection of type System.String, using the specified separator between each member. - /// This is a shortcut for string.Join(...) - /// - /// A collection that contains the strings to concatenate. - /// The string to use as a separator. separator is included in the returned string only if values has more than one element. - /// A string that consists of the members of values delimited by the separator string. If values has no members, the method returns System.String.Empty. - public static string JoinAsString(this IEnumerable source, string separator) - { - return string.Join(separator, source); - } - - /// - /// Concatenates the members of a collection, using the specified separator between each member. - /// This is a shortcut for string.Join(...) - /// - /// A collection that contains the objects to concatenate. - /// The string to use as a separator. separator is included in the returned string only if values has more than one element. - /// The type of the members of values. - /// A string that consists of the members of values delimited by the separator string. If values has no members, the method returns System.String.Empty. - public static string JoinAsString(this IEnumerable source, string separator) - { - return string.Join(separator, source); - } - - /// - /// Filters a by given predicate if given condition is true. - /// - /// Enumerable to apply filtering - /// A boolean value - /// Predicate to filter the enumerable - /// Filtered or not filtered enumerable based on - public static IEnumerable WhereIf(this IEnumerable source, bool condition, Func predicate) - { - return condition - ? source.Where(predicate) - : source; - } - - /// - /// Filters a by given predicate if given condition is true. - /// - /// Enumerable to apply filtering - /// A boolean value - /// Predicate to filter the enumerable - /// Filtered or not filtered enumerable based on - public static IEnumerable WhereIf(this IEnumerable source, bool condition, Func predicate) - { - return condition - ? source.Where(predicate) - : source; - } - - /// - /// This method is used to try to get a value in a dictionary if it does exists. - /// - /// Type of the value - /// The collection object - /// Key - /// Value of the key (or default value if key not exists) - /// True if key does exists in the dictionary - internal static bool TryGetValue(this IDictionary dictionary, string key, out T value) - { - if (dictionary.TryGetValue(key, out var valueObj) && valueObj is T result) - { - value = result; - return true; - } - - value = default; - return false; - } - - /// - /// Gets a value from the dictionary with given key. Returns default value if can not find. - /// - /// Dictionary to check and get - /// Key to find the value - /// Type of the key - /// Type of the value - /// Value if found, default if can not found. - public static TValue GetOrDefault(this Dictionary dictionary, TKey key) - { - return dictionary.TryGetValue(key, out var obj) ? obj : default; - } - - /// - /// Gets a value from the dictionary with given key. Returns default value if can not find. - /// - /// Dictionary to check and get - /// Key to find the value - /// Type of the key - /// Type of the value - /// Value if found, default if can not found. - public static TValue GetOrDefault(this IDictionary dictionary, TKey key) - { - return dictionary.TryGetValue(key, out var obj) ? obj : default; - } - - /// - /// Gets a value from the dictionary with given key. Returns default value if can not find. - /// - /// Dictionary to check and get - /// Key to find the value - /// Type of the key - /// Type of the value - /// Value if found, default if can not found. - public static TValue GetOrDefault(this IReadOnlyDictionary dictionary, TKey key) - { - return dictionary.TryGetValue(key, out var obj) ? obj : default; - } - - /// - /// Gets a value from the dictionary with given key. Returns default value if can not find. - /// - /// Dictionary to check and get - /// Key to find the value - /// Type of the key - /// Type of the value - /// Value if found, default if can not found. - public static TValue GetOrDefault(this ConcurrentDictionary dictionary, TKey key) - { - return dictionary.TryGetValue(key, out var obj) ? obj : default; - } - - /// - /// Gets a value from the dictionary with given key. Returns default value if can not find. - /// - /// Dictionary to check and get - /// Key to find the value - /// A factory method used to create the value if not found in the dictionary - /// Type of the key - /// Type of the value - /// Value if found, default if can not found. - public static TValue GetOrAdd(this IDictionary dictionary, TKey key, Func factory) - { - if (dictionary.TryGetValue(key, out var obj)) - { - return obj; - } - - return dictionary[key] = factory(key); - } - - /// - /// Gets a value from the dictionary with given key. Returns default value if can not find. - /// - /// Dictionary to check and get - /// Key to find the value - /// A factory method used to create the value if not found in the dictionary - /// Type of the key - /// Type of the value - /// Value if found, default if can not found. - public static TValue GetOrAdd(this IDictionary dictionary, TKey key, Func factory) - { - return dictionary.GetOrAdd(key, _ => factory()); - } - - /// - /// Adds an item to the collection if it's not already in the collection. - /// - /// The collection - /// Item to check and add - /// Type of the items in the collection - /// Returns True if added, returns False if not. - public static bool AddIfNotContains([NotNull] this ICollection source, T item) - { - Check.EnsureNotNull(source, nameof(source)); - - if (source.Contains(item)) - { - return false; - } - - source.Add(item); - return true; - } - - /// - /// Adds items to the collection which are not already in the collection. - /// - /// The collection - /// Item to check and add - /// Type of the items in the collection - /// Returns the added items. - public static IEnumerable AddIfNotContains([NotNull] this ICollection source, IEnumerable items) - { - Check.EnsureNotNull(source, nameof(source)); - - var addedItems = new List(); - - foreach (var item in items) - { - if (source.Contains(item)) - { - continue; - } - - source.Add(item); - addedItems.Add(item); - } - - return addedItems; - } - - /// - /// Adds an item to the collection if it's not already in the collection based on the given . - /// - /// The collection - /// The condition to decide if the item is already in the collection - /// A factory that returns the item - /// Type of the items in the collection - /// Returns True if added, returns False if not. - public static bool AddIfNotContains([NotNull] this ICollection source, [NotNull] Func predicate, [NotNull] Func itemFactory) - { - Check.EnsureNotNull(source, nameof(source)); - Check.EnsureNotNull(predicate, nameof(predicate)); - Check.EnsureNotNull(itemFactory, nameof(itemFactory)); - - if (source.Any(predicate)) - { - return false; - } - - source.Add(itemFactory()); - return true; - } - - /// - /// Removes all items from the collection those satisfy the given . - /// - /// Type of the items in the collection - /// The collection - /// The condition to remove the items - /// List of removed items - public static IList RemoveAll([NotNull] this ICollection source, Func predicate) - { - var items = source.Where(predicate).ToList(); - - foreach (var item in items) - { - source.Remove(item); - } - - return items; - } - - /// - /// Removes all items from the collection those satisfy the given - /// predicate - /// - /// . - /// - /// Type of the items in the collection - /// The collection - /// Items to be removed from the list - public static void RemoveAll([NotNull] this ICollection source, IEnumerable items) - { - foreach (var item in items) - { - source.Remove(item); - } - } - - /// - /// - /// - /// - /// - /// - /// - /// - /// - public static IDictionary Set(this IDictionary dictionary, TKey key, TValue value) - { - dictionary[key] = value; - return dictionary; - } - - /// - /// - /// - /// - /// - /// - /// - /// - /// - public static void TryGetValue(this IDictionary dictionary, TKey key, Action func) - { - if (!dictionary.TryGetValue(key, out var value)) - { - return; - } - - var refValue = (TRef)System.Convert.ChangeType(value, typeof(TRef)); - func(refValue); - } - - /// - /// - /// - /// - /// - /// - /// - /// - public static void TryGetValue(this IDictionary dictionary, TKey key, Action func) - { - if (!dictionary.TryGetValue(key, out var value)) - { - return; - } - - var refValue = (TRef)value; - func(refValue); - } - - /// - /// - /// - /// - /// - /// - /// - /// - public static void TryGetValue(this IDictionary dictionary, TKey key, Action func) - { - if (!dictionary.TryGetValue(key, out var value)) - { - return; - } - - func(value); - } - - /// - /// - /// - /// - /// - /// - /// - /// - public static TValue GetValue(this IDictionary dictionary, string key, StringComparison comparison) - { - var item = dictionary.FirstOrDefault(t => t.Key.Equals(key, comparison)); - return item.Value; - } - - /// - /// Try get value for specified key from dictionary. - /// - /// The key type. - /// The value type. - /// - /// - /// - public static TValue TryGetValue(this IDictionary source, TKey key) - { - if (source == null) - { - throw new NullReferenceException(); - } - - return source.TryGetValue(key, out var value) ? value : default; - } - - /// - /// Try get value for specified key from dictionary. - /// - /// The key type. - /// The value type. - /// - /// - /// The defaut value if key doesn't exists. - /// - /// - public static TValue TryGetValue(this IDictionary source, TKey key, TValue defaultValue) - { - if (source == null) - { - throw new NullReferenceException(); - } - - return source.TryGetValue(key, out var value) ? value : defaultValue; - } - - /// - /// Try get exists value for specified key or set value if not exists. - /// - /// The type of the key. - /// The type of the value. - /// The source. - /// The key. - /// The value. - /// Throws if is null. - public static TValue TryGetOrSetValue(this IDictionary source, TKey key) - { - if (source == null) - { - throw new NullReferenceException(); - } - - if (source.TryGetValue(key, out var value)) - { - return value; - } - - source.Add(key, value); - return source[key]; - } - - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public static TValue TryGetValue(this IDictionary source, string key, TValue defaultValue, StringComparison comparison) - { - if (source == null) - { - throw new NullReferenceException(); - } - - return source.Keys.Contains(key, comparison) ? source.FirstOrDefault(t => t.Key.Equals(key, comparison)).Value : defaultValue; - } - - /// - /// Gets index of item. - /// - /// - /// - /// - /// - /// - public static int IndexOf(this IEnumerable enumerable, T item) - { - if (enumerable == null) - throw new ArgumentNullException(nameof(enumerable)); - - var i = 0; - foreach (var element in enumerable) - { - if (Equals(element, item)) - { - return i; - } - - i++; - } - - return -1; - } - - /// - /// Gets index of item which matches the predicate. - /// - /// - /// - /// - /// - public static int IndexOf(this IEnumerable enumerable, Func predicate) - { - var i = 0; - foreach (var element in enumerable) - { - if (predicate(element)) - { - return i; - } - - i++; - } - - return -1; - } + private static readonly Random _random = new(); + + /// + /// Performs the specified action on each element of the + /// + /// The type of the element. + /// The source. + /// The System.Action`1 delegate to perform on each element of the . + /// Throws if is null. + /// Throws if action is null. + public static void ForEach(this IEnumerable source, Action action) + { + if (source == null) + { + throw new NullReferenceException(); + } + + ArgumentAssert.ThrowIfNull(action, nameof(action)); + + foreach (var value in source) + { + action(value); + } + } + + /// + /// Determines whether the collection contains the object. + /// + /// The source. + /// The value. + /// The comparison. + /// true if [contains] [the specified value]; otherwise, false. + /// + public static bool Contains(this IEnumerable source, string value, StringComparison comparison) + { + if (source == null) + { + throw new NullReferenceException(); + } + + return source.Any(t => t.Equals(value, comparison)); + } + + /// + /// Determines whether the specified collection [is null or empty]. + /// + /// The source. + /// true if the specified source [is null or empty]; otherwise, false. + public static bool IsNullOrEmpty(this IEnumerable source) + { + if (source == null) + { + return true; + } + + return !source.GetEnumerator().MoveNext(); + } + + /// + /// Determines whether a sequence is null or empty. + /// + /// + /// A sequence in which to locate a value. + /// + public static bool IsNullOrEmpty(this IEnumerable source) + { + return source == null || !source.Any(); + } + + /// + /// Determine whether the specified collection is equals to another. + /// + /// The type of the collection item. + /// The source. + /// The dest. + /// true if collection is equals to another, false otherwise. + /// + /// dest + public static bool Equals(this IEnumerable source, IEnumerable dest) where T : IComparable + { + if (source == null) + { + throw new NullReferenceException(); + } + + ArgumentAssert.ThrowIfNull(dest, nameof(dest)); + + return dest.Count() == source.Count() && source.All(dest.Contains); + } + + /// + /// Concatenates the elements of an object array, using the specified separator between each element. + /// + /// Member type. + /// A collection that contains the objects to concatenate. + /// + /// + public static string Join(this IEnumerable values, string separator) + { + if (values == null) + { + throw new NullReferenceException(); + } + + return string.Join(separator, values); + } + + /// + /// Concatenates the members of a collection, using the specified separator between each member. + /// + /// Member type. + /// A collection that contains the objects to concatenate. + /// The string to use as a separator.separator is included in the returned string only if values has more than one element. + /// + /// + /// + public static string Join(this IEnumerable values, string separator, int startIndex, int count) + { + if (values == null) + { + throw new NullReferenceException(); + } + + if (startIndex >= values.Count()) + { + throw new IndexOutOfRangeException(); + } + + return values.Skip(startIndex).Take(count).Join(separator); + } + + /// + /// Convert Pageable collection to view collection. + /// + /// + /// + /// + public static ViewCollection ToView(this PageableCollection source) where T : class, new() + { + if (source == null) + { + throw new NullReferenceException(); + } + + return new ViewCollection(source.ToArray(), source.TotalCount); + } + + /// + /// Read to a pageable collection. + /// + /// The type of the element. + /// The source. + /// The total count. + /// The index. + /// The size. + /// A new pageable collection contains all elements of . + /// Throws if source is null. + public static PageableCollection Paginate(this IList source, long totalCount, int index, int size) + { + if (source == null) + { + throw new NullReferenceException(); + } + + return new PageableCollection(source) { TotalCount = totalCount, PageNumber = index, PageSize = size }; + } + + /// + /// Converts an exists pageable collection to another. + /// + /// The type of collection items. + /// The source. + /// The index. + /// The size. + /// A new pageable collection. + /// Throw if source is null. + public static PageableCollection Convert(this PageableCollection source, int index, int size) + { + if (source == null) + { + throw new NullReferenceException(nameof(source)); + } + + return new PageableCollection(source) { TotalCount = source.TotalCount, PageNumber = index, PageSize = size }; + } + + /// + /// + /// + /// + /// + /// + public static IEnumerable Shuffle(this IEnumerable enumerable) + { + var buffer = enumerable.ToList(); + + for (var i = 0; i < buffer.Count; i++) + { + var j = _random.Next(i, buffer.Count); + + yield return buffer[j]; + + buffer[j] = buffer[i]; + } + } + + /// + /// + /// + /// + /// + /// + public static ObservableCollection ToObservable(this IEnumerable source) + { + var collection = new ObservableCollection(source); + return collection; + } + + /// + /// Reify the specified collection. + /// + /// + /// + /// + /// + public static IReadOnlyCollection Reify(this IEnumerable source) + { + return source switch + { + null => throw new NullReferenceException(), + IReadOnlyCollection result => result, + ICollection collection => new CollectionWrapper(collection), + ICollection nonGenericCollection => new NonGenericCollectionWrapper(nonGenericCollection), + _ => new List(source) + }; + } + + /// + /// + /// + /// + /// + /// + /// + public static void InsertRange(this IList source, int index, IEnumerable items) + { + foreach (var item in items) + { + source.Insert(index++, item); + } + } + + /// + /// + /// + /// + /// + /// + /// + public static int FindIndex(this IList source, Predicate selector) + { + for (var i = 0; i < source.Count; ++i) + { + if (selector(source[i])) + { + return i; + } + } + + return -1; + } + + /// + /// + /// + /// + /// + /// + public static void AddFirst(this IList source, T item) + { + source.Insert(0, item); + } + + /// + /// + /// + /// + /// + /// + public static void AddLast(this IList source, T item) + { + source.Insert(source.Count, item); + } + + /// + /// + /// + /// + /// + /// + /// + public static void InsertAfter(this IList source, T existingItem, T item) + { + var index = source.IndexOf(existingItem); + if (index < 0) + { + source.AddFirst(item); + return; + } + + source.Insert(index + 1, item); + } + + /// + /// + /// + /// + /// + /// + /// + public static void InsertAfter(this IList source, Predicate selector, T item) + { + var index = source.FindIndex(selector); + if (index < 0) + { + source.AddFirst(item); + return; + } + + source.Insert(index + 1, item); + } + + /// + /// + /// + /// + /// + /// + /// + public static void InsertBefore(this IList source, T existingItem, T item) + { + var index = source.IndexOf(existingItem); + if (index < 0) + { + source.AddLast(item); + return; + } + + source.Insert(index, item); + } + + /// + /// + /// + /// + /// + /// + /// + public static void InsertBefore(this IList source, Predicate selector, T item) + { + var index = source.FindIndex(selector); + if (index < 0) + { + source.AddLast(item); + return; + } + + source.Insert(index, item); + } + + /// + /// + /// + /// + /// + /// + /// + public static void ReplaceWhile(this IList source, Predicate selector, T item) + { + for (var i = 0; i < source.Count; i++) + { + if (selector(source[i])) + { + source[i] = item; + } + } + } + + /// + /// + /// + /// + /// + /// + /// + public static void ReplaceWhile(this IList source, Predicate selector, Func itemFactory) + { + for (var i = 0; i < source.Count; i++) + { + var item = source[i]; + if (selector(item)) + { + source[i] = itemFactory(item); + } + } + } + + /// + /// + /// + /// + /// + /// + /// + public static void ReplaceOne(this IList source, Predicate selector, T item) + { + for (var i = 0; i < source.Count; i++) + { + if (selector(source[i])) + { + source[i] = item; + return; + } + } + } + + /// + /// + /// + /// + /// + /// + /// + public static void ReplaceOne(this IList source, Predicate selector, Func itemFactory) + { + for (var i = 0; i < source.Count; i++) + { + var item = source[i]; + if (!selector(item)) + { + continue; + } + + source[i] = itemFactory(item); + return; + } + } + + /// + /// + /// + /// + /// + /// + /// + public static void ReplaceOne(this IList source, T item, T replaceWith) + { + for (var i = 0; i < source.Count; i++) + { + if (Comparer.Default.Compare(source[i], item) != 0) + { + continue; + } + + source[i] = replaceWith; + return; + } + } + + /// + /// + /// + /// + /// + /// + /// + /// + public static void MoveItem(this List source, Predicate selector, int targetIndex) + { + if (!targetIndex.IsBetween(0, source.Count - 1)) + { + throw new IndexOutOfRangeException("targetIndex should be between 0 and " + (source.Count - 1)); + } + + var currentIndex = source.FindIndex(0, selector); + if (currentIndex == targetIndex) + { + return; + } + + var item = source[currentIndex]; + source.RemoveAt(currentIndex); + source.Insert(targetIndex, item); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public static T GetOrAdd([NotNull] this IList source, Func selector, Func factory) + { + Check.EnsureNotNull(source, nameof(source)); + + var item = source.FirstOrDefault(selector); + + if (item == null) + { + item = factory(); + source.Add(item); + } + + return item; + } + + /// + /// Sort a list by a topological sorting, which consider their dependencies. + /// + /// The type of the members of values. + /// A list of objects to sort + /// Function to resolve the dependencies + /// Equality comparer for dependencies + /// + /// Returns a new list ordered by dependencies. + /// If A depends on B, then B will come before than A in the resulting list. + /// + public static List SortByDependencies( + this IEnumerable source, + Func> getDependencies, + IEqualityComparer comparer = null) + { + /* See: http://www.codeproject.com/Articles/869059/Topological-sorting-in-Csharp + * http://en.wikipedia.org/wiki/Topological_sorting + */ + + var sorted = new List(); + var visited = new Dictionary(comparer); + + foreach (var item in source) + { + SortByDependenciesVisit(item, getDependencies, sorted, visited); + } + + return sorted; + } + + /// + /// + /// + /// The type of the members of values. + /// Item to resolve + /// Function to resolve the dependencies + /// List with the sorted items + /// Dictionary with the visited items + private static void SortByDependenciesVisit(T item, Func> getDependencies, IList sorted, Dictionary visited) + { + var alreadyVisited = visited.TryGetValue(item, out var inProcess); + + if (alreadyVisited) + { + if (inProcess) + { + throw new ArgumentException("Cyclic dependency found! Item: " + item); + } + } + else + { + visited[item] = true; + + var dependencies = getDependencies(item); + if (dependencies != null) + { + foreach (var dependency in dependencies) + { + SortByDependenciesVisit(dependency, getDependencies, sorted, visited); + } + } + + visited[item] = false; + sorted.Add(item); + } + } + + /// + /// Concatenates the members of a constructed collection of type System.String, using the specified separator between each member. + /// This is a shortcut for string.Join(...) + /// + /// A collection that contains the strings to concatenate. + /// The string to use as a separator. separator is included in the returned string only if values has more than one element. + /// A string that consists of the members of values delimited by the separator string. If values has no members, the method returns System.String.Empty. + public static string JoinAsString(this IEnumerable source, string separator) + { + return string.Join(separator, source); + } + + /// + /// Concatenates the members of a collection, using the specified separator between each member. + /// This is a shortcut for string.Join(...) + /// + /// A collection that contains the objects to concatenate. + /// The string to use as a separator. separator is included in the returned string only if values has more than one element. + /// The type of the members of values. + /// A string that consists of the members of values delimited by the separator string. If values has no members, the method returns System.String.Empty. + public static string JoinAsString(this IEnumerable source, string separator) + { + return string.Join(separator, source); + } + + /// + /// Filters a by given predicate if given condition is true. + /// + /// Enumerable to apply filtering + /// A boolean value + /// Predicate to filter the enumerable + /// Filtered or not filtered enumerable based on + public static IEnumerable WhereIf(this IEnumerable source, bool condition, Func predicate) + { + return condition + ? source.Where(predicate) + : source; + } + + /// + /// Filters a by given predicate if given condition is true. + /// + /// Enumerable to apply filtering + /// A boolean value + /// Predicate to filter the enumerable + /// Filtered or not filtered enumerable based on + public static IEnumerable WhereIf(this IEnumerable source, bool condition, Func predicate) + { + return condition + ? source.Where(predicate) + : source; + } + + /// + /// This method is used to try to get a value in a dictionary if it does exists. + /// + /// Type of the value + /// The collection object + /// Key + /// Value of the key (or default value if key not exists) + /// True if key does exists in the dictionary + internal static bool TryGetValue(this IDictionary dictionary, string key, out T value) + { + if (dictionary.TryGetValue(key, out var valueObj) && valueObj is T result) + { + value = result; + return true; + } + + value = default; + return false; + } + + /// + /// Gets a value from the dictionary with given key. Returns default value if can not find. + /// + /// Dictionary to check and get + /// Key to find the value + /// Type of the key + /// Type of the value + /// Value if found, default if can not found. + public static TValue GetOrDefault(this Dictionary dictionary, TKey key) + { + return dictionary.TryGetValue(key, out var obj) ? obj : default; + } + + /// + /// Gets a value from the dictionary with given key. Returns default value if can not find. + /// + /// Dictionary to check and get + /// Key to find the value + /// Type of the key + /// Type of the value + /// Value if found, default if can not found. + public static TValue GetOrDefault(this IDictionary dictionary, TKey key) + { + return dictionary.TryGetValue(key, out var obj) ? obj : default; + } + + /// + /// Gets a value from the dictionary with given key. Returns default value if can not find. + /// + /// Dictionary to check and get + /// Key to find the value + /// Type of the key + /// Type of the value + /// Value if found, default if can not found. + public static TValue GetOrDefault(this IReadOnlyDictionary dictionary, TKey key) + { + return dictionary.TryGetValue(key, out var obj) ? obj : default; + } + + /// + /// Gets a value from the dictionary with given key. Returns default value if can not find. + /// + /// Dictionary to check and get + /// Key to find the value + /// Type of the key + /// Type of the value + /// Value if found, default if can not found. + public static TValue GetOrDefault(this ConcurrentDictionary dictionary, TKey key) + { + return dictionary.TryGetValue(key, out var obj) ? obj : default; + } + + /// + /// Gets a value from the dictionary with given key. Returns default value if can not find. + /// + /// Dictionary to check and get + /// Key to find the value + /// A factory method used to create the value if not found in the dictionary + /// Type of the key + /// Type of the value + /// Value if found, default if can not found. + public static TValue GetOrAdd(this IDictionary dictionary, TKey key, Func factory) + { + if (dictionary.TryGetValue(key, out var obj)) + { + return obj; + } + + return dictionary[key] = factory(key); + } + + /// + /// Gets a value from the dictionary with given key. Returns default value if can not find. + /// + /// Dictionary to check and get + /// Key to find the value + /// A factory method used to create the value if not found in the dictionary + /// Type of the key + /// Type of the value + /// Value if found, default if can not found. + public static TValue GetOrAdd(this IDictionary dictionary, TKey key, Func factory) + { + return dictionary.GetOrAdd(key, _ => factory()); + } + + /// + /// Adds an item to the collection if it's not already in the collection. + /// + /// The collection + /// Item to check and add + /// Type of the items in the collection + /// Returns True if added, returns False if not. + public static bool AddIfNotContains([NotNull] this ICollection source, T item) + { + Check.EnsureNotNull(source, nameof(source)); + + if (source.Contains(item)) + { + return false; + } + + source.Add(item); + return true; + } + + /// + /// Adds items to the collection which are not already in the collection. + /// + /// The collection + /// Item to check and add + /// Type of the items in the collection + /// Returns the added items. + public static IEnumerable AddIfNotContains([NotNull] this ICollection source, IEnumerable items) + { + Check.EnsureNotNull(source, nameof(source)); + + var addedItems = new List(); + + foreach (var item in items) + { + if (source.Contains(item)) + { + continue; + } + + source.Add(item); + addedItems.Add(item); + } + + return addedItems; + } + + /// + /// Adds an item to the collection if it's not already in the collection based on the given . + /// + /// The collection + /// The condition to decide if the item is already in the collection + /// A factory that returns the item + /// Type of the items in the collection + /// Returns True if added, returns False if not. + public static bool AddIfNotContains([NotNull] this ICollection source, [NotNull] Func predicate, [NotNull] Func itemFactory) + { + Check.EnsureNotNull(source, nameof(source)); + Check.EnsureNotNull(predicate, nameof(predicate)); + Check.EnsureNotNull(itemFactory, nameof(itemFactory)); + + if (source.Any(predicate)) + { + return false; + } + + source.Add(itemFactory()); + return true; + } + + /// + /// Removes all items from the collection those satisfy the given . + /// + /// Type of the items in the collection + /// The collection + /// The condition to remove the items + /// List of removed items + public static IList RemoveAll([NotNull] this ICollection source, Func predicate) + { + var items = source.Where(predicate).ToList(); + + foreach (var item in items) + { + source.Remove(item); + } + + return items; + } + + /// + /// Removes all items from the collection those satisfy the given + /// predicate + /// + /// . + /// + /// Type of the items in the collection + /// The collection + /// Items to be removed from the list + public static void RemoveAll([NotNull] this ICollection source, IEnumerable items) + { + foreach (var item in items) + { + source.Remove(item); + } + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static IDictionary Set(this IDictionary dictionary, TKey key, TValue value) + { + dictionary[key] = value; + return dictionary; + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static void TryGetValue(this IDictionary dictionary, TKey key, Action func) + { + if (!dictionary.TryGetValue(key, out var value)) + { + return; + } + + var refValue = (TRef)System.Convert.ChangeType(value, typeof(TRef)); + func(refValue); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public static void TryGetValue(this IDictionary dictionary, TKey key, Action func) + { + if (!dictionary.TryGetValue(key, out var value)) + { + return; + } + + var refValue = (TRef)value; + func(refValue); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public static void TryGetValue(this IDictionary dictionary, TKey key, Action func) + { + if (!dictionary.TryGetValue(key, out var value)) + { + return; + } + + func(value); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public static TValue GetValue(this IDictionary dictionary, string key, StringComparison comparison) + { + var item = dictionary.FirstOrDefault(t => t.Key.Equals(key, comparison)); + return item.Value; + } + + /// + /// Try get value for specified key from dictionary. + /// + /// The key type. + /// The value type. + /// + /// + /// + public static TValue TryGetValue(this IDictionary source, TKey key) + { + if (source == null) + { + throw new NullReferenceException(); + } + + return source.TryGetValue(key, out var value) ? value : default; + } + + /// + /// Try get value for specified key from dictionary. + /// + /// The key type. + /// The value type. + /// + /// + /// The defaut value if key doesn't exists. + /// + /// + public static TValue TryGetValue(this IDictionary source, TKey key, TValue defaultValue) + { + if (source == null) + { + throw new NullReferenceException(); + } + + return source.TryGetValue(key, out var value) ? value : defaultValue; + } + + /// + /// Try get exists value for specified key or set value if not exists. + /// + /// The type of the key. + /// The type of the value. + /// The source. + /// The key. + /// The value. + /// Throws if is null. + public static TValue TryGetOrSetValue(this IDictionary source, TKey key) + { + if (source == null) + { + throw new NullReferenceException(); + } + + if (source.TryGetValue(key, out var value)) + { + return value; + } + + source.Add(key, value); + return source[key]; + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static TValue TryGetValue(this IDictionary source, string key, TValue defaultValue, StringComparison comparison) + { + if (source == null) + { + throw new NullReferenceException(); + } + + return source.Keys.Contains(key, comparison) ? source.FirstOrDefault(t => t.Key.Equals(key, comparison)).Value : defaultValue; + } + + /// + /// Gets index of item. + /// + /// + /// + /// + /// + /// + public static int IndexOf(this IEnumerable enumerable, T item) + { + if (enumerable == null) + throw new ArgumentNullException(nameof(enumerable)); + + var i = 0; + foreach (var element in enumerable) + { + if (Equals(element, item)) + { + return i; + } + + i++; + } + + return -1; + } + + /// + /// Gets index of item which matches the predicate. + /// + /// + /// + /// + /// + public static int IndexOf(this IEnumerable enumerable, Func predicate) + { + var i = 0; + foreach (var element in enumerable) + { + if (predicate(element)) + { + return i; + } + + i++; + } + + return -1; + } } \ No newline at end of file diff --git a/Source/Euonia.Core/Reflection/Reflect.cs b/Source/Euonia.Core/Reflection/Reflect.cs index d250674..8f8c82a 100644 --- a/Source/Euonia.Core/Reflection/Reflect.cs +++ b/Source/Euonia.Core/Reflection/Reflect.cs @@ -21,10 +21,7 @@ public static class Reflect /// Or, the property is static. public static PropertyInfo GetProperty(Expression> expression) { - if (expression == null) - { - throw new ArgumentNullException(nameof(expression)); - } + ArgumentAssert.ThrowIfNull(expression, nameof(expression)); if (expression.Body is not MemberExpression memberExpression) { @@ -62,10 +59,7 @@ public static PropertyInfo GetProperty(Expression> expression /// public static PropertyInfo GetProperty(Expression> expression) { - if (expression == null) - { - throw new ArgumentNullException(nameof(expression)); - } + ArgumentAssert.ThrowIfNull(expression, nameof(expression)); PropertyInfo result; @@ -659,7 +653,9 @@ public static PropertyInfo GetProperty(Expression> { var info = Reflect.GetMemberInfo(expression) as PropertyInfo; if (info == null) + { throw new ArgumentException("Member is not a property"); + } return info; } @@ -673,7 +669,9 @@ public static FieldInfo GetField(Expression> expression) { var info = Reflect.GetMemberInfo(expression) as FieldInfo; if (info == null) + { throw new ArgumentException("Member is not a field"); + } return info; } @@ -688,10 +686,7 @@ public static FieldInfo GetField(Expression> expression) /// property public static void SetValue(TTarget item, TValue value, Expression> property) { - if (property == null) - { - throw new ArgumentNullException(nameof(property)); - } + ArgumentAssert.ThrowIfNull(property, nameof(property)); var propertyInfo = GetProperty(property); diff --git a/Source/Euonia.Modularity/Dependency/ILazyServiceProvider.cs b/Source/Euonia.Modularity/Dependency/ILazyServiceProvider.cs index 8ec6a0e..e64fa0d 100644 --- a/Source/Euonia.Modularity/Dependency/ILazyServiceProvider.cs +++ b/Source/Euonia.Modularity/Dependency/ILazyServiceProvider.cs @@ -1,7 +1,7 @@ namespace System; /// -/// +/// Represents a service provider that can be used to retrieve services with lazy initialization. /// public interface ILazyServiceProvider { @@ -42,7 +42,7 @@ public interface ILazyServiceProvider T GetService(T defaultValue); /// - /// + /// Get service of type from the . /// /// /// @@ -92,7 +92,7 @@ public interface ILazyServiceProvider /// /// Get service of type from the . /// - /// + /// An object that specifies the type of service object to get. /// An object that specifies the key of service object to get. /// object GetRequiredKeyedService(Type serviceType, object serviceKey); @@ -118,7 +118,7 @@ public interface ILazyServiceProvider /// /// Get service of type from the . /// - /// + /// An object that specifies the type of service object to get. /// An object that specifies the key of service object to get. /// /// @@ -127,7 +127,7 @@ public interface ILazyServiceProvider /// /// Get service of type from the . /// - /// + /// An object that specifies the type of service object to get. /// An object that specifies the key of service object to get. /// /// diff --git a/Source/Euonia.Modularity/Dependency/LazyServiceProvider.cs b/Source/Euonia.Modularity/Dependency/LazyServiceProvider.cs index 4252300..26df8f2 100644 --- a/Source/Euonia.Modularity/Dependency/LazyServiceProvider.cs +++ b/Source/Euonia.Modularity/Dependency/LazyServiceProvider.cs @@ -1,4 +1,5 @@ using System.Collections.Concurrent; +using Microsoft.Extensions.DependencyInjection; using Nerosoft.Euonia.Modularity; namespace System; @@ -33,33 +34,14 @@ public virtual T GetRequiredService() return (T)GetRequiredService(typeof(T)); } - /// - /// - /// - /// - /// - /// - /// + /// public virtual object GetRequiredService(Type serviceType) { return CachedServices.GetOrAdd(new ServiceIdentifier(serviceType), _ => new Lazy(() => { - if (ServiceProvider == null) - { - throw new ArgumentNullException(nameof(ServiceProvider)); - } - - if (serviceType == null) - { - throw new ArgumentNullException(nameof(serviceType)); - } - - var service = ServiceProvider.GetService(serviceType); - if (service == null) - { - throw new NullReferenceException(nameof(serviceType)); - } - + ArgumentAssert.ThrowIfNull(ServiceProvider, nameof(ServiceProvider)); + ArgumentAssert.ThrowIfNull(serviceType, nameof(serviceType)); + var service = ServiceProvider.GetRequiredService(serviceType); return service; })).Value; } diff --git a/Source/Euonia.Modularity/Dependency/ServiceIdentifier.cs b/Source/Euonia.Modularity/Dependency/ServiceIdentifier.cs index bcb47b9..f337162 100644 --- a/Source/Euonia.Modularity/Dependency/ServiceIdentifier.cs +++ b/Source/Euonia.Modularity/Dependency/ServiceIdentifier.cs @@ -1,16 +1,34 @@ namespace Nerosoft.Euonia.Modularity; +/// +/// The service identifier. +/// internal readonly struct ServiceIdentifier : IEquatable { + /// + /// Gets the service key. + /// public object ServiceKey { get; } + /// + /// Gets the service type. + /// public Type ServiceType { get; } + /// + /// Initializes a new instance of the struct. + /// + /// public ServiceIdentifier(Type serviceType) { ServiceType = serviceType; } + /// + /// Initializes a new instance of the struct. + /// + /// + /// public ServiceIdentifier(object serviceKey, Type serviceType) { ServiceKey = serviceKey; @@ -27,6 +45,7 @@ public bool Equals(ServiceIdentifier other) { return ServiceType == other.ServiceType && ServiceKey.Equals(other.ServiceKey); } + return false; } @@ -41,9 +60,10 @@ public override int GetHashCode() { return ServiceType.GetHashCode(); } + unchecked { return (ServiceType.GetHashCode() * 397) ^ ServiceKey.GetHashCode(); } } -} +} \ No newline at end of file