mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
* Version: 162.2.1
* Fix CollectionExtensions.TryGetValue erroring for indexes under 0
* Revert "Version: 162.2.1"
This reverts commit 9b7f4d48cf.
* release notes
---------
Co-authored-by: ElectroJr <leonsfriedrich@gmail.com>
288 lines
12 KiB
C#
288 lines
12 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Linq;
|
|
using System.Runtime.InteropServices;
|
|
|
|
namespace Robust.Shared.Utility
|
|
{
|
|
public static class Extensions
|
|
{
|
|
/// <summary>
|
|
/// Ensures that the specified array has the specified length.
|
|
/// </summary>
|
|
public static void EnsureLength<T>(ref T[] array, int length)
|
|
{
|
|
if (array.Length > length)
|
|
return;
|
|
|
|
Array.Resize(ref array, length);
|
|
}
|
|
|
|
public static IList<T> Clone<T>(this IList<T> listToClone) where T : ICloneable
|
|
{
|
|
var clone = new List<T>(listToClone.Count);
|
|
|
|
foreach (var value in listToClone)
|
|
{
|
|
clone.Add((T) value.Clone());
|
|
}
|
|
|
|
return clone;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a shallow clone of a list.
|
|
/// Basically a new list with all the same elements.
|
|
/// </summary>
|
|
/// <param name="self">The list to shallow clone.</param>
|
|
/// <typeparam name="T">The type of the list's elements.</typeparam>
|
|
/// <returns>A new list with the same elements as <paramref name="list" />.</returns>
|
|
public static List<T> ShallowClone<T>(this List<T> self)
|
|
{
|
|
var list = new List<T>(self.Count);
|
|
list.AddRange(self);
|
|
return list;
|
|
}
|
|
|
|
public static Dictionary<TKey, TValue> ShallowClone<TKey, TValue>(this Dictionary<TKey, TValue> self)
|
|
where TKey : notnull
|
|
{
|
|
var dict = new Dictionary<TKey, TValue>(self.Count);
|
|
foreach (var item in self)
|
|
{
|
|
dict[item.Key] = item.Value;
|
|
}
|
|
return dict;
|
|
}
|
|
|
|
public static bool TryGetValue<T>(this IList<T> list, int index, out T value)
|
|
{
|
|
if (index >= 0 && list.Count > index)
|
|
{
|
|
value = list[index];
|
|
return true;
|
|
}
|
|
|
|
value = default!;
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Remove an item from the list, replacing it with the one at the very end of the list.
|
|
/// This means that the order will not be preserved, but it should be an O(1) operation.
|
|
/// </summary>
|
|
/// <param name="index">The index to remove</param>
|
|
/// <returns>The removed element</returns>
|
|
public static T RemoveSwap<T>(this IList<T> list, int index)
|
|
{
|
|
// This method has no implementation details,
|
|
// and changing the result of an operation is a breaking change.
|
|
var old = list[index];
|
|
var replacement = list[list.Count - 1];
|
|
list[index] = replacement;
|
|
// TODO: Any more efficient way to pop the last element off?
|
|
list.RemoveAt(list.Count - 1);
|
|
return old;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Pop an element from the end of a list, removing it from the list and returning it.
|
|
/// </summary>
|
|
/// <param name="list">The list to pop from.</param>
|
|
/// <typeparam name="T">The type of the elements of the list.</typeparam>
|
|
/// <returns>The popped off element.</returns>
|
|
/// <exception cref="InvalidOperationException">
|
|
/// Thrown if the list is empty.
|
|
/// </exception>
|
|
public static T Pop<T>(this IList<T> list)
|
|
{
|
|
if (list.Count == 0)
|
|
{
|
|
throw new InvalidOperationException();
|
|
}
|
|
|
|
var t = list[list.Count - 1];
|
|
list.RemoveAt(list.Count-1);
|
|
return t;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Just like <see cref="Enumerable.FirstOrDefault{TSource}(System.Collections.Generic.IEnumerable{TSource}, Func{TSource, bool})"/> but returns null for value types as well.
|
|
/// </summary>
|
|
/// <param name="source">An <see cref="T:System.Collections.Generic.IEnumerable`1" /> to return an element from.</param>
|
|
/// <param name="predicate">A function to test each element for a condition.</param>
|
|
/// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
|
|
/// <returns> null if <paramref name="source" /> is empty or if no element passes the test specified by <paramref name="predicate" />; otherwise, the first element in <paramref name="source" /> that passes the test specified by <paramref name="predicate" />.</returns>
|
|
/// <exception cref="T:System.ArgumentNullException">
|
|
/// <paramref name="source" /> or <paramref name="predicate" /> is <see langword="null" />.</exception>
|
|
public static TSource? FirstOrNull<TSource>(
|
|
this IEnumerable<TSource> source,
|
|
Func<TSource, bool> predicate)
|
|
where TSource: struct
|
|
{
|
|
if (source == null)
|
|
throw new ArgumentNullException(nameof (source));
|
|
if (predicate == null)
|
|
throw new ArgumentNullException(nameof (predicate));
|
|
foreach (TSource source1 in source)
|
|
{
|
|
if (predicate(source1))
|
|
return source1;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Just like <see cref="Enumerable.FirstOrDefault{TSource}(System.Collections.Generic.IEnumerable{TSource})"/> but returns null for value types as well.
|
|
/// </summary>
|
|
/// <param name="source">
|
|
/// An <see cref="T:System.Collections.Generic.IEnumerable`1" /> to return the first element from.
|
|
/// </param>
|
|
/// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
|
|
/// <returns>
|
|
/// <see langword="null" /> if <paramref name="source" /> is empty, otherwise,
|
|
/// the first element in <paramref name="source" />.
|
|
/// </returns>
|
|
/// <exception cref="T:System.ArgumentNullException">
|
|
/// <paramref name="source" /> is <see langword="null" />.</exception>
|
|
public static TSource? FirstOrNull<TSource>(this IEnumerable<TSource> source)
|
|
where TSource : struct
|
|
{
|
|
if (source == null)
|
|
throw new ArgumentNullException(nameof (source));
|
|
|
|
using var enumerator = source.GetEnumerator();
|
|
if (!enumerator.MoveNext())
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return enumerator.Current;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Just like <see cref="Enumerable.FirstOrDefault{TSource}(System.Collections.Generic.IEnumerable{TSource}, Func{TSource, bool})"/> but returns null for value types as well.
|
|
/// </summary>
|
|
/// <param name="source">An <see cref="T:System.Collections.Generic.IEnumerable`1" /> to return an element from.</param>
|
|
/// <param name="predicate">A function to test each element for a condition.</param>
|
|
/// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
|
|
/// <returns>True if an element has been found.</returns>
|
|
/// <exception cref="T:System.ArgumentNullException">
|
|
/// <paramref name="source" /> or <paramref name="predicate" /> is <see langword="null" />.</exception>
|
|
public static bool TryFirstOrNull<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate, [NotNullWhen(true)] out TSource? element) where TSource : struct
|
|
{
|
|
element = source.FirstOrNull(predicate);
|
|
return element != null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Just like <see cref="Enumerable.FirstOrDefault{TSource}(System.Collections.Generic.IEnumerable{TSource}, Func{TSource, bool})"/> but returns null for value types as well.
|
|
/// </summary>
|
|
/// <param name="source">An <see cref="T:System.Collections.Generic.IEnumerable`1" /> to return an element from.</param>
|
|
/// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
|
|
/// <returns>True if an element has been found.</returns>
|
|
/// <exception cref="T:System.ArgumentNullException">
|
|
/// <paramref name="source" /> is <see langword="null" />.</exception>
|
|
public static bool TryFirstOrNull<TSource>(this IEnumerable<TSource> source, [NotNullWhen(true)] out TSource? element) where TSource : struct
|
|
{
|
|
return TryFirstOrNull(source, _ => true, out element);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Wraps Linq's FirstOrDefault.
|
|
/// </summary>
|
|
/// <param name="source">An <see cref="T:System.Collections.Generic.IEnumerable`1" /> to return an element from.</param>
|
|
/// <param name="predicate">A function to test each element for a condition.</param>
|
|
/// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
|
|
/// <returns>True if an element has been found.</returns>
|
|
/// <exception cref="T:System.ArgumentNullException">
|
|
/// <paramref name="source" /> or <paramref name="predicate" /> is <see langword="null" />.</exception>
|
|
public static bool TryFirstOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate, [NotNullWhen(true)] out TSource? element) where TSource : class
|
|
{
|
|
element = source.FirstOrDefault(predicate);
|
|
return element != null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Wraps Linq's FirstOrDefault.
|
|
/// </summary>
|
|
/// <param name="source">An <see cref="T:System.Collections.Generic.IEnumerable`1" /> to return an element from.</param>
|
|
/// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
|
|
/// <returns>True if an element has been found.</returns>
|
|
/// <exception cref="T:System.ArgumentNullException">
|
|
/// <paramref name="source" /> is <see langword="null" />.</exception>
|
|
public static bool TryFirstOrDefault<TSource>(this IEnumerable<TSource> source, [NotNullWhen(true)] out TSource? element) where TSource : class
|
|
{
|
|
return TryFirstOrDefault(source, _ => true, out element);
|
|
}
|
|
|
|
public static TValue GetOrNew<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key) where TValue : new()
|
|
where TKey : notnull
|
|
{
|
|
if (!dict.TryGetValue(key, out var value))
|
|
{
|
|
value = new TValue();
|
|
dict.Add(key, value);
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
public static TValue GetOrNew<TKey, TValue>(this Dictionary<TKey, TValue> dict, TKey key)
|
|
where TValue : new()
|
|
where TKey : notnull
|
|
{
|
|
ref var entry = ref CollectionsMarshal.GetValueRefOrAddDefault(dict, key, out var exists);
|
|
if (!exists)
|
|
entry = new TValue();
|
|
|
|
return entry!;
|
|
}
|
|
|
|
public static TValue GetOrNew<TKey, TValue>(this Dictionary<TKey, TValue> dict, TKey key, out bool exists)
|
|
where TValue : new()
|
|
where TKey : notnull
|
|
{
|
|
ref var entry = ref CollectionsMarshal.GetValueRefOrAddDefault(dict, key, out exists);
|
|
if (!exists)
|
|
entry = new TValue();
|
|
|
|
return entry!;
|
|
}
|
|
|
|
// More efficient than LINQ.
|
|
public static KeyValuePair<TKey, TValue>[] ToArray<TKey, TValue>(this Dictionary<TKey, TValue> dict)
|
|
where TKey : notnull
|
|
{
|
|
var array = new KeyValuePair<TKey, TValue>[dict.Count];
|
|
|
|
var i = 0;
|
|
foreach (var kvPair in dict)
|
|
{
|
|
array[i] = kvPair;
|
|
i += 1;
|
|
}
|
|
|
|
return array;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tries to get a value from a dictionary and checks if that value is of type T
|
|
/// </summary>
|
|
/// <typeparam name="T">The type that sould be casted to</typeparam>
|
|
/// <returns>Whether the value was present in the dictionary and of the required type</returns>
|
|
public static bool TryCastValue<T, TKey, TValue>(this Dictionary<TKey, TValue> dict, TKey key, [NotNullWhen(true)] out T? value) where TKey : notnull
|
|
{
|
|
if (dict.TryGetValue(key, out var untypedValue) && untypedValue is T typedValue)
|
|
{
|
|
value = typedValue;
|
|
return true;
|
|
}
|
|
|
|
value = default;
|
|
return false;
|
|
}
|
|
}
|
|
}
|