// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace Animancer
{
///
/// A wrapper which allows fast reference equality checks and dictionary usage
/// by ensuring that users of identical strings are given the same
/// instead of needing to compare each character in the strings.
///
///
/// Rather than a constructor, instances of this class are acquired via
/// or via implicit conversion from (which calls the same method).
///
/// Unlike UnityEngine.InputSystem.Utilities.InternedString,
/// this implementation is case-sensitive and treats null and "" as not equal.
/// It's also a class to allow usage as a key in a dictionary keyed by without boxing.
///
/// Example:
///
/// public static readonly StringReference MyStringReference = "My String";
///
///
/// https://kybernetik.com.au/animancer/api/Animancer/StringReference
public class StringReference :
IComparable,
IConvertable
{
/************************************************************************************************************************/
/// The encapsulated .
/// This field will never be null.
public readonly string String;
/************************************************************************************************************************/
private static readonly Dictionary
StringToReference = new(256);
/// Returns a containing the `value`.
///
/// The returned reference is cached and the same one will be
/// returned each time this method is called with the same `value`.
///
/// Returns null if the `value` is null.
///
/// The `value` is case sensitive.
///
public static StringReference Get(string value)
{
if (value is null)
return null;
if (!StringToReference.TryGetValue(value, out var reference))
StringToReference.Add(value, reference = new(value));
// This system could be made case insensitive based on a static bool.
// If true, convert the value to lower case for the dictionary key but still reference the original.
// When changing the setting, rebuild the dictionary with the appropriate keys.
return reference;
}
/************************************************************************************************************************/
/// Creates a new array of s to the `strings`.
public static StringReference[] Get(params string[] strings)
{
if (strings == null)
return null;
if (strings.Length == 0)
return Array.Empty();
var references = new StringReference[strings.Length];
for (int i = 0; i < strings.Length; i++)
references[i] = strings[i];
return references;
}
/************************************************************************************************************************/
/// Returns a containing the `value` if one has already been created.
/// The `value` is case sensitive.
public static bool TryGet(string value, out StringReference reference)
{
if (value is not null && StringToReference.TryGetValue(value, out reference))
return true;
reference = null;
return false;
}
/************************************************************************************************************************/
/// Creates a new .
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private StringReference(string value)
=> String = value;
/// Calls .
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator StringReference(string value)
=> Get(value);
/// [Internal]
/// Returns a new which will not be shared by regular calls to
/// .
///
///
/// This means the reference will never be equal to others
/// even if they contain the same .
///
internal static StringReference Unique(string value)
=> new(value);
/************************************************************************************************************************/
/// Returns the .
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override string ToString()
=> String;
/// Returns the .
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator string(StringReference value)
=> value?.String;
/************************************************************************************************************************/
///
string IConvertable.Convert()
=> String;
/************************************************************************************************************************/
/// Compares the s.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int CompareTo(StringReference other)
=> String.CompareTo(other?.String);
/************************************************************************************************************************/
}
/// Extension methods for .
public static class StringReferenceExtensions
{
/************************************************************************************************************************/
/// Is the `reference` null or its empty?
/// Similar to .
public static bool IsNullOrEmpty(this StringReference reference)
=> reference is null
|| reference.String.Length == 0;
/************************************************************************************************************************/
///
/// Is the equal to the `other`
/// when treating "" as equal to null?
///
public static bool EqualsWhereEmptyIsNull(this StringReference reference, StringReference other)
{
if (reference == other)
return true;
else if (reference == null)
return other.String.Length == 0;
else if (reference.String.Length == 0)
return other == null;
else
return false;
}
/************************************************************************************************************************/
/// Creates a new array containing the s.
public static string[] ToStrings(this StringReference[] references)
{
if (references == null)
return null;
if (references.Length == 0)
return Array.Empty();
var strings = new string[references.Length];
for (int i = 0; i < references.Length; i++)
strings[i] = references[i];
return strings;
}
/************************************************************************************************************************/
}
}