using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Runtime.InteropServices;
namespace Robust.Shared.Utility
{
public static class Extensions
{
///
/// Ensures that the specified array has the specified length.
///
public static void EnsureLength(ref T[] array, int length)
{
if (array.Length >= length)
return;
Array.Resize(ref array, length);
}
public static IList Clone(this IList listToClone) where T : ICloneable
{
var clone = new List(listToClone.Count);
foreach (var value in listToClone)
{
clone.Add((T) value.Clone());
}
return clone;
}
///
/// Creates a shallow clone of a list.
/// Basically a new list with all the same elements.
///
/// The list to shallow clone.
/// The type of the list's elements.
/// A new list with the same elements as .
public static List ShallowClone(this List self)
{
return new List(self);
}
public static Dictionary ShallowClone(this Dictionary self)
where TKey : notnull
{
return new Dictionary(self, self.Comparer);
}
///
/// Compares the entries inside of 2 dictionaries to check equality.
///
///
/// The base Equals implementation checks references hence this.
///
///
///
///
///
///
public static bool DictionaryEquals(
this IReadOnlyDictionary self,
IReadOnlyDictionary other)
where TKey : notnull
{
if (ReferenceEquals(self, other))
return true;
if (self.Count != other.Count)
return false;
var valueComparer = EqualityComparer.Default;
foreach (var (key, value) in self)
{
if (!other.TryGetValue(key, out var otherValue) || !valueComparer.Equals(value, otherValue))
return false;
}
return true;
}
public static bool TryGetValue(this IList list, int index, out T value)
{
if (index >= 0 && list.Count > index)
{
value = list[index];
return true;
}
value = default!;
return false;
}
///
/// 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.
///
/// The index to remove
/// The removed element
public static T RemoveSwap(this IList 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;
}
///
/// Pop an element from the end of a list, removing it from the list and returning it.
///
/// The list to pop from.
/// The type of the elements of the list.
/// The popped off element.
///
/// Thrown if the list is empty.
///
public static T Pop(this IList list)
{
if (list.Count == 0)
{
throw new InvalidOperationException();
}
var t = list[list.Count - 1];
list.RemoveAt(list.Count-1);
return t;
}
///
/// Just like but returns null for value types as well.
///
/// An to return an element from.
/// A function to test each element for a condition.
/// The type of the elements of .
/// null if is empty or if no element passes the test specified by ; otherwise, the first element in that passes the test specified by .
///
/// or is .
public static TSource? FirstOrNull(
this IEnumerable source,
Func 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;
}
///
/// Just like but returns null for value types as well.
///
///
/// An to return the first element from.
///
/// The type of the elements of .
///
/// if is empty, otherwise,
/// the first element in .
///
///
/// is .
public static TSource? FirstOrNull(this IEnumerable 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;
}
///
/// Just like but returns null for value types as well.
///
/// An to return an element from.
/// A function to test each element for a condition.
/// The type of the elements of .
/// True if an element has been found.
///
/// or is .
public static bool TryFirstOrNull(this IEnumerable source, Func predicate, [NotNullWhen(true)] out TSource? element) where TSource : struct
{
element = source.FirstOrNull(predicate);
return element != null;
}
///
/// Just like but returns null for value types as well.
///
/// An to return an element from.
/// The type of the elements of .
/// True if an element has been found.
///
/// is .
public static bool TryFirstOrNull(this IEnumerable source, [NotNullWhen(true)] out TSource? element) where TSource : struct
{
element = source.FirstOrNull();
return element != null;
}
///
/// Wraps Linq's FirstOrDefault.
///
/// An to return an element from.
/// A function to test each element for a condition.
/// The type of the elements of .
/// True if an element has been found.
///
/// or is .
public static bool TryFirstOrDefault(this IEnumerable source, Func predicate, [NotNullWhen(true)] out TSource? element) where TSource : class
{
element = source.FirstOrDefault(predicate);
return element != null;
}
///
/// Wraps Linq's FirstOrDefault.
///
/// An to return an element from.
/// The type of the elements of .
/// True if an element has been found.
///
/// is .
public static bool TryFirstOrDefault(this IEnumerable source, [NotNullWhen(true)] out TSource? element) where TSource : class
{
element = source.FirstOrDefault();
return element != null;
}
public static TValue GetOrNew(this IDictionary 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(this Dictionary 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(this Dictionary 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!;
}
[Pure]
public static KeyValuePair[] ToArray(this Dictionary dict)
where TKey : notnull
{
// Faster than the old custom implementation
// 0 count faster, 1 count slower, any higher faster (on my 7800X3D).
return Enumerable.ToArray(dict);
}
///
/// Tries to get a value from a dictionary and checks if that value is of type T
///
/// The type that sould be casted to
/// Whether the value was present in the dictionary and of the required type
public static bool TryCastValue(this Dictionary 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;
}
}
}