//========= Copyright 2016-2023, HTC Corporation. All rights reserved. =========== using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; using UnityEngine; using UnityEngine.Serialization; namespace HTC.UnityPlugin.Utility { [AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)] public class InvalidEnumArrayIndexAttribute : Attribute { } public abstract class EnumToIntResolver { } /// /// A base generic class that provide function that converts Enum value into int value. /// /// /// /// Resolver should provide faster function to convert from TEnum into int value /// If not defined, EnumArrayBase static constructor can only provide slower convert function like "(int)(object)enumValue" or "EqualityComparer.Default.GetHashCode(enumValue)" /// /// /// /// class MyEnumResolver : EnumToIntResolver /// { /// public override int Resolve(MyEnum e) { return (int)e; } /// } /// /// /// /// Define custom resolver class for each enum type in your project, EnumToIntResolverCache will find it and instantiate/cache their instance /// /// /// public abstract class EnumToIntResolver : EnumToIntResolver #if CSHARP_7_OR_LATER where TEnum : Enum #endif { public abstract int Resolve(TEnum e); } public static class EnumToIntResolverCache { private static Dictionary typeCache; private static Dictionary instanceCache; public static void ClearCache() { typeCache = null; instanceCache = null; } public static bool TryGetCachedResolverType(out Type resolverType) #if CSHARP_7_OR_LATER where TEnum : Enum #endif { if (typeCache == null) { typeCache = new Dictionary(); FindAllResolverTypesInAllDomain(typeCache); } return typeCache.TryGetValue(typeof(TEnum), out resolverType); } public static bool TryGetCachedResolverInstance(out EnumToIntResolver resolver) #if CSHARP_7_OR_LATER where TEnum : Enum #endif { var enumType = typeof(TEnum); var resolverBase = default(EnumToIntResolver); if (instanceCache != null && instanceCache.TryGetValue(enumType, out resolverBase)) { resolver = resolverBase as EnumToIntResolver; return true; } else { Type resolverType; if (TryGetCachedResolverType(out resolverType)) { resolver = (EnumToIntResolver)Activator.CreateInstance(resolverType); if (instanceCache == null) { instanceCache = new Dictionary(); } instanceCache[enumType] = resolver; return true; } resolver = null; return false; } } public static int FindAllResolverTypesInAllDomain(IDictionary outDict) { var count = 0; var resolverTypeDef = typeof(EnumToIntResolver<>); var resolverBaseType = typeof(EnumToIntResolver); var currentAsm = resolverTypeDef.Assembly; var currentAsmName = currentAsm.GetName().Name; foreach (var asm in AppDomain.CurrentDomain.GetAssemblies()) { var referencingCurrentAsm = false; if (asm == currentAsm) { referencingCurrentAsm = true; } else { foreach (var asmref in asm.GetReferencedAssemblies()) { if (asmref.Name == currentAsmName) { referencingCurrentAsm = true; break; } } } if (referencingCurrentAsm) { // try find valid role enum type in assembly foreach (var type in asm.GetTypes()) { if (type.IsAbstract) { continue; } if (!type.IsSubclassOf(resolverBaseType)) { continue; } if (!type.BaseType.IsGenericType) { continue; } if (type.BaseType.GetGenericTypeDefinition() != resolverTypeDef) { continue; } foreach (var typeParam in type.BaseType.GetGenericArguments()) { #if !CSHARP_7_OR_LATER if (!typeParam.IsEnum) { break; } #endif if (!outDict.ContainsKey(typeParam)) { outDict[typeParam] = type; ++count; //Debug.Log("Found reslover type (" + type.FullName + ") for " + typeParam.Name); } break; } } } } return count; } } [Serializable] public abstract class EnumArrayBase { public const int DEFAULT_LENGTH_LIMIT = 1024; protected delegate bool RangeCheckFunc(object e, out int ei); public abstract Type EnumType { get; } public abstract Type ElementType { get; } public abstract int MinInt { get; } public abstract int MaxInt { get; } public abstract int Length { get; } public abstract int Capacity { get; } public abstract string EnumName(int enumInt); public abstract string EnumNameWithAlias(int enumInt); public abstract bool IsValidIndex(int enumInt); public abstract void Clear(); public abstract void FillCapacityToLength(); public abstract void TrimCapacityToLength(); protected static bool WithoutInvalidIndexAttr(FieldInfo fi) { return fi.IsStatic && fi.GetCustomAttributes(typeof(InvalidEnumArrayIndexAttribute), true).Length == 0; } protected static int CompareMetadataToken(FieldInfo fi) { return fi.MetadataToken; } protected static bool RangeCheckFromUInt8(object e, out int ei) { ei = (byte)e; return true; } protected static bool RangeCheckFromInt8(object e, out int ei) { ei = (sbyte)e; return true; } protected static bool RangeCheckFromInt16(object e, out int ei) { ei = (short)e; return true; } protected static bool RangeCheckFromUInt16(object e, out int ei) { ei = (ushort)e; return true; } protected static bool RangeCheckFromInt32(object e, out int ei) { ei = (int)e; return true; } protected static bool RangeCheckFromUInt32(object e, out int ei) { var l = (uint)e; if (l <= int.MaxValue) { ei = (int)l; return true; } else { ei = int.MaxValue; return false; } } protected static bool RangeCheckFromInt64(object e, out int ei) { var l = (long)e; if (l < int.MinValue) { ei = int.MinValue; return false; } else if (l <= int.MaxValue) { ei = (int)l; return true; } else { ei = int.MaxValue; return false; } } protected static bool RangeCheckFromUInt64(object e, out int ei) { var l = (ulong)e; if (l <= int.MaxValue) { ei = (int)l; return true; } else { ei = int.MaxValue; return false; } } } [Serializable] public abstract class EnumArrayBase : EnumArrayBase #if CSHARP_7_OR_LATER where TEnum : Enum #endif { public struct EnumEnumerator : IEnumerator, IEnumerable { private readonly int iStart; private readonly int iEnd; private int iCurrent; object IEnumerator.Current { get { return Current; } } public TEnum Current { get { return InternalI2E(iCurrent); } } public static EnumEnumerator All { get { return new EnumEnumerator(0, StaticLength - 1); } } public static EnumEnumerator From(TEnum from) { var ifrom = E2I(from) - StaticMinInt; return new EnumEnumerator(ifrom, Mathf.Max(ifrom, StaticLength - 1)); } public static EnumEnumerator FromTo(TEnum from, TEnum to) { return new EnumEnumerator(E2I(from) - StaticMinInt, E2I(to) - StaticMinInt); } private EnumEnumerator(int from, int to) { if (StaticLength == 0) { iStart = iEnd = iCurrent = -1; } else { from = Mathf.Clamp(from, 0, StaticLength - 1); to = Mathf.Clamp(to, 0, StaticLength - 1); if (from <= to) { iStart = iCurrent = from - 1; iEnd = to; } else { iStart = iCurrent = from + 1; iEnd = to; } } } void IDisposable.Dispose() { } public void Reset() { iCurrent = iStart; } public EnumEnumerator GetEnumerator() { return this; } IEnumerator IEnumerable.GetEnumerator() { return this; } IEnumerator IEnumerable.GetEnumerator() { return this; } public bool MoveNext() { while (iCurrent != iEnd) { iCurrent = iCurrent > iEnd ? (iCurrent - 1) : (iCurrent + 1); if (InternalStaticIsValidIndex(iCurrent)) { return true; } } return false; } } private static readonly TEnum[] enums; private static readonly string[] enumNames; private static readonly string[] enumNameWithAliases; private static Func funcE2I; public static readonly int StaticMinInt; public static readonly TEnum StaticMin; public static readonly int StaticMaxInt; public static readonly TEnum StaticMax; public static readonly int StaticLength; public static readonly Type StaticEnumType = typeof(TEnum); static EnumArrayBase() { #if !CSHARP_7_OR_LATER if (!StaticEnumType.IsEnum) { throw new Exception(StaticEnumType.Name + " is not enum type!"); } #endif var underlyingType = Enum.GetUnderlyingType(StaticEnumType); var rangeCheckFunc = default(RangeCheckFunc); if (underlyingType == typeof(int)) { rangeCheckFunc = RangeCheckFromInt32; } else if (underlyingType == typeof(uint)) { rangeCheckFunc = RangeCheckFromUInt32; } else if (underlyingType == typeof(long)) { rangeCheckFunc = RangeCheckFromInt64; } else if (underlyingType == typeof(ulong)) { rangeCheckFunc = RangeCheckFromUInt64; } else if (underlyingType == typeof(byte)) { rangeCheckFunc = RangeCheckFromUInt8; } else if (underlyingType == typeof(sbyte)) { rangeCheckFunc = RangeCheckFromInt8; } else if (underlyingType == typeof(short)) { rangeCheckFunc = RangeCheckFromInt16; } else if (underlyingType == typeof(ushort)) { rangeCheckFunc = RangeCheckFromUInt16; } // find out min/max/length value in defined enum values var fields = StaticEnumType.GetFields().Where(WithoutInvalidIndexAttr).OrderBy((Func)CompareMetadataToken).ToArray(); var fieldEnums = new TEnum[fields.Length]; var fieldValues = new int[fields.Length]; var fieldNames = new string[fields.Length]; var validIndexFound = false; for (int i = 0, imax = fields.Length; i < imax; ++i) { var field = fields[i]; var vo = field.GetValue(null); var ve = (TEnum)vo; int vi; if (rangeCheckFunc(vo, out vi)) { if (!validIndexFound) { validIndexFound = true; StaticMinInt = StaticMaxInt = vi; StaticMin = StaticMax = ve; } else { if (vi < StaticMinInt) { if (StaticMaxInt >= int.MinValue + DEFAULT_LENGTH_LIMIT && vi <= StaticMaxInt - DEFAULT_LENGTH_LIMIT) { Debug.Log("[EnumArray] too small. vi=" + vi + " min=" + StaticMinInt + " max=" + StaticMaxInt); continue; // length out of range } StaticMinInt = vi; StaticMin = ve; } else if (vi > StaticMaxInt) { if (StaticMinInt <= int.MaxValue - DEFAULT_LENGTH_LIMIT && vi >= StaticMinInt + DEFAULT_LENGTH_LIMIT) { Debug.Log("[EnumArray] too large. vi=" + vi + " min=" + StaticMinInt + " max=" + StaticMaxInt); continue; // length out of range } StaticMaxInt = vi; StaticMax = ve; } } fieldValues[i] = vi; fieldEnums[i] = ve; fieldNames[i] = field.Name; } } if (!validIndexFound) { Debug.LogWarning("Valid index for EnumArray not found. type:" + StaticEnumType.Name); StaticMinInt = 0; StaticMin = default(TEnum); StaticMaxInt = 0; StaticMax = default(TEnum); StaticLength = 0; } StaticLength = StaticMaxInt - StaticMinInt + 1; // create an int array with invalid enum values enums = new TEnum[StaticLength]; enumNames = new string[StaticLength]; enumNameWithAliases = new string[StaticLength]; for (int fi = 0, imax = fields.Length; fi < imax; ++fi) { if (fieldNames[fi] == null) { continue; } var i = fieldValues[fi] - StaticMinInt; if (enumNames[i] == null) { enums[i] = fieldEnums[fi]; enumNames[i] = fieldNames[fi]; } else if (enumNameWithAliases[i] == null) { enumNameWithAliases[i] = fieldNames[fi]; } else { enumNameWithAliases[i] += ", " + fieldNames[fi]; } } for (int i = 0, imax = StaticLength; i < imax; ++i) { if (enumNames[i] != null) { if (enumNameWithAliases[i] != null) { enumNameWithAliases[i] = enumNames[i] + " (" + enumNameWithAliases[i] + ")"; } else { enumNameWithAliases[i] = enumNames[i]; } } } } public override Type EnumType { get { return StaticEnumType; } } public override string EnumName(int enumInt) { return StaticEnumName(enumInt); } public override string EnumNameWithAlias(int enumInt) { return StaticEnumNameWithAlias(enumInt); } public static string StaticEnumName(TEnum e) { return StaticEnumName(E2I(e)); } public static string StaticEnumName(int enumInt) { return enumNames[enumInt - StaticMinInt]; } public static string StaticEnumNameWithAlias(int enumInt) { return enumNameWithAliases[enumInt - StaticMinInt]; } public override bool IsValidIndex(int enumInt) { return StaticIsValidIndex(enumInt); } public static bool StaticIsValidIndex(TEnum e) { return StaticIsValidIndex(E2I(e)); } public static bool StaticIsValidIndex(int enumInt) { var i = enumInt - StaticMinInt; return i >= 0 && i < StaticLength && InternalStaticIsValidIndex(i); } protected static bool InternalStaticIsValidIndex(int index) { return enumNames[index] != null; } public static EnumEnumerator StaticEnums { get { return EnumEnumerator.All; } } public static EnumEnumerator StaticEnumsFrom(TEnum from) { return EnumEnumerator.From(from); } public static EnumEnumerator StaticEnumsFrom(TEnum from, TEnum to) { return EnumEnumerator.FromTo(from, to); } public static void InitializeFuncE2I() { if (funcE2I != null) { return; } // Find first found custom resolver // resolver should provide faster function to convert TEnum to int value // most common & fasteast converting is to directly cast in the script like "return (int)enumValue;" // In this generic constructor, we can only use slower converting function like "(int)(object)enumValue" or "EqualityComparer.Default.GetHashCode(enumValue)" EnumToIntResolver resolver; if (EnumToIntResolverCache.TryGetCachedResolverInstance(out resolver)) { funcE2I = resolver.Resolve; return; } WarnBoxingResolver(); var underlyingType = Enum.GetUnderlyingType(StaticEnumType); if (underlyingType == typeof(int)) { funcE2I = EqualityComparer.Default.GetHashCode; } else if (underlyingType == typeof(uint)) { funcE2I = e => (int)(uint)(object)e; } else if (underlyingType == typeof(long)) { funcE2I = e => (int)(long)(object)e; } else if (underlyingType == typeof(ulong)) { funcE2I = e => (int)(ulong)(object)e; } else if (underlyingType == typeof(byte)) { funcE2I = e => (byte)(object)e; } else if (underlyingType == typeof(sbyte)) { funcE2I = e => (sbyte)(object)e; } else if (underlyingType == typeof(short)) { funcE2I = e => (short)(object)e; } else if (underlyingType == typeof(ushort)) { funcE2I = e => (ushort)(object)e; } } public static int E2I(TEnum e) { InitializeFuncE2I(); return funcE2I(e); } public static TEnum I2E(int ei) { return InternalI2E(ei - StaticMinInt); } protected static TEnum InternalI2E(int index) { return enums[index]; } private static void WarnBoxingResolver() { Debug.LogWarning("Boxing Resolver for enum " + StaticEnumType.Name + " is used. Define subclass of class EnumToIntResolver<" + StaticEnumType.Name + "> to provide faster resolver instead."); } } [Serializable] public class EnumArray : EnumArrayBase, IEnumerable #if CSHARP_7_OR_LATER where TEnum : Enum #endif { public interface IReadOnly : IEnumerable { Type EnumType { get; } Type ElementType { get; } TEnum Min { get; } TEnum Max { get; } int MinInt { get; } int MaxInt { get; } TValue this[TEnum e] { get; } TValue this[int ev] { get; } new ValueEnumerator GetEnumerator(); ValueEnumerator Values { get; } ValueEnumerator ValuesFrom(TEnum from); ValueEnumerator ValuesFrom(TEnum from, TEnum to); EnumEnumerator Enums { get; } EnumEnumerator EnumsFrom(TEnum from); EnumEnumerator EnumsFrom(TEnum from, TEnum to); EnumValueEnumerator EnumValues { get; } EnumValueEnumerator EnumValuesFrom(TEnum from); EnumValueEnumerator EnumValuesFrom(TEnum from, TEnum to); /// /// Length between min and max TEnum value /// int Length { get; } /// /// Real length for the underlying array /// (underlying array could be overridden with unexpected length by Unity's serialization) /// int Capacity { get; } } [Serializable] private class ReadOnlyEnumArray : IReadOnly { public readonly EnumArray source; public ReadOnlyEnumArray(EnumArray source) { this.source = source; } public Type EnumType { get { return source.EnumType; } } public Type ElementType { get { return source.ElementType; } } public TEnum Min { get { return source.Min; } } public TEnum Max { get { return source.Max; } } public int MinInt { get { return source.MinInt; } } public int MaxInt { get { return source.MaxInt; } } public int Length { get { return source.Length; } } public int Capacity { get { return source.Capacity; } } public TValue this[TEnum e] { get { return source[e]; } } public TValue this[int ev] { get { return source[ev]; } } public ValueEnumerator GetEnumerator() { return source.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return source.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return source.GetEnumerator(); } public ValueEnumerator Values { get { return source.Values; } } public ValueEnumerator ValuesFrom(TEnum from) { return source.ValuesFrom(from); } public ValueEnumerator ValuesFrom(TEnum from, TEnum to) { return source.ValuesFrom(from, to); } public EnumEnumerator Enums { get { return source.Enums; } } public EnumEnumerator EnumsFrom(TEnum from) { return source.EnumsFrom(from); } public EnumEnumerator EnumsFrom(TEnum from, TEnum to) { return source.EnumsFrom(from, to); } public EnumValueEnumerator EnumValues { get { return source.EnumValues; } } public EnumValueEnumerator EnumValuesFrom(TEnum from) { return source.EnumValuesFrom(from); } public EnumValueEnumerator EnumValuesFrom(TEnum from, TEnum to) { return source.EnumValuesFrom(from, to); } } public struct ValueEnumerator : IEnumerator, IEnumerable { private readonly TValue[] elements; private readonly int iStart; private readonly int iEnd; private int iCurrent; object IEnumerator.Current { get { return Current; } } public TValue Current { get { return elements[iCurrent]; } } public static ValueEnumerator All(EnumArray array) { return new ValueEnumerator(array, 0, StaticLength - 1); } public static ValueEnumerator From(EnumArray array, TEnum from) { var ifrom = E2I(from) - StaticMinInt; return new ValueEnumerator(array, ifrom, Mathf.Max(ifrom, StaticLength - 1)); } public static ValueEnumerator FromTo(EnumArray array, TEnum from, TEnum to) { return new ValueEnumerator(array, E2I(from) - StaticMinInt, E2I(to) - StaticMinInt); } private ValueEnumerator(EnumArray array, int from, int to) { if (StaticLength == 0) { elements = null; iStart = iEnd = iCurrent = -1; } else { from = Mathf.Clamp(from, 0, StaticLength - 1); to = Mathf.Clamp(to, 0, StaticLength - 1); if (from <= to) { iStart = iCurrent = from - 1; iEnd = to; } else { iStart = iCurrent = from + 1; iEnd = to; } array.FillCapacityToLength(); elements = array.elements; } } void IDisposable.Dispose() { } public void Reset() { iCurrent = iStart; } public ValueEnumerator GetEnumerator() { return this; } IEnumerator IEnumerable.GetEnumerator() { return this; } IEnumerator IEnumerable.GetEnumerator() { return this; } public bool MoveNext() { if (elements != null) { while (iCurrent != iEnd) { iCurrent = iCurrent > iEnd ? (iCurrent - 1) : (iCurrent + 1); if (InternalStaticIsValidIndex(iCurrent)) { return true; } } } return false; } } public struct EnumValueEnumerator : IEnumerator>, IEnumerable> { private readonly TValue[] elements; private readonly int iStart; private readonly int iEnd; private int iCurrent; object IEnumerator.Current { get { return Current; } } public KeyValuePair Current { get { return new KeyValuePair(InternalI2E(iCurrent), elements[iCurrent]); } } public static EnumValueEnumerator All(EnumArray array) { return new EnumValueEnumerator(array, 0, StaticLength - 1); } public static EnumValueEnumerator From(EnumArray array, TEnum from) { var ifrom = E2I(from) - StaticMinInt; return new EnumValueEnumerator(array, ifrom, Mathf.Max(ifrom, StaticLength - 1)); } public static EnumValueEnumerator FromTo(EnumArray array, TEnum from, TEnum to) { return new EnumValueEnumerator(array, E2I(from) - StaticMinInt, E2I(to) - StaticMinInt); } private EnumValueEnumerator(EnumArray array, int from, int to) { if (StaticLength == 0) { elements = null; iStart = iEnd = iCurrent = -1; } else { from = Mathf.Clamp(from, 0, StaticLength - 1); to = Mathf.Clamp(to, 0, StaticLength - 1); if (from <= to) { iStart = iCurrent = from - 1; iEnd = to; } else { iStart = iCurrent = from + 1; iEnd = to; } array.FillCapacityToLength(); elements = array.elements; } } void IDisposable.Dispose() { } public void Reset() { iCurrent = iStart; } public EnumValueEnumerator GetEnumerator() { return this; } IEnumerator> IEnumerable>.GetEnumerator() { return this; } IEnumerator IEnumerable.GetEnumerator() { return this; } public bool MoveNext() { if (elements != null) { while (iCurrent != iEnd) { iCurrent = iCurrent > iEnd ? (iCurrent - 1) : (iCurrent + 1); if (InternalStaticIsValidIndex(iCurrent)) { return true; } } } return false; } } [SerializeField] [FormerlySerializedAs("m_array")] private TValue[] elements; private ReadOnlyEnumArray readOnly; public readonly static Type StaticElementType = typeof(TValue); public EnumArray() { elements = new TValue[StaticLength]; } public EnumArray(TValue initValue) : this() { Clear(initValue); } public override Type ElementType { get { return StaticElementType; } } public override int MinInt { get { return StaticMinInt; } } public override int MaxInt { get { return StaticMaxInt; } } public TEnum Min { get { return StaticMin; } } public TEnum Max { get { return StaticMax; } } /// /// Length between min and max TEnum value /// public override int Length { get { return StaticLength; } } /// /// Real length for the underlying array /// (underlying array could be overridden with unexpected length by Unity's serialization) /// public override int Capacity { get { return elements == null ? 0 : elements.Length; } } public IReadOnly ReadOnly { get { return readOnly != null ? readOnly : (readOnly = new ReadOnlyEnumArray(this)); } } public ValueEnumerator GetEnumerator() { return Values; } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public ValueEnumerator Values { get { return ValueEnumerator.All(this); } } public ValueEnumerator ValuesFrom(TEnum from) { return ValueEnumerator.From(this, from); } public ValueEnumerator ValuesFrom(TEnum from, TEnum to) { return ValueEnumerator.FromTo(this, from, to); } public EnumEnumerator Enums { get { return StaticEnums; } } public EnumEnumerator EnumsFrom(TEnum from) { return StaticEnumsFrom(from); } public EnumEnumerator EnumsFrom(TEnum from, TEnum to) { return StaticEnumsFrom(from, to); } public EnumValueEnumerator EnumValues { get { return EnumValueEnumerator.All(this); } } public EnumValueEnumerator EnumValuesFrom(TEnum from) { return EnumValueEnumerator.From(this, from); } public EnumValueEnumerator EnumValuesFrom(TEnum from, TEnum to) { return EnumValueEnumerator.FromTo(this, from, to); } public TValue this[TEnum e] { get { return this[E2I(e)]; } set { this[E2I(e)] = value; } } public TValue this[int ei] { get { FillCapacityToLength(); return elements[ei - StaticMinInt]; } set { FillCapacityToLength(); elements[ei - StaticMinInt] = value; } } public override void Clear() { if (elements != null && elements.Length > 0) { Array.Clear(elements, 0, elements.Length); } } public void Clear(TValue clearWith) { FillCapacityToLength(); for (int i = elements.Length - 1; i >= 0; --i) { elements[i] = clearWith; } } public void CopyFrom(EnumArray source) { if (ReferenceEquals(this, source)) { return; } FillCapacityToLength(); source.FillCapacityToLength(); Array.Copy(source.elements, 0, elements, 0, StaticLength); } public static void Copy(EnumArray srcArray, TEnum srcEnumIndex, EnumArray dstArray, TEnum dstEnumIndex, int length) { Copy(srcArray, E2I(srcEnumIndex), dstArray, E2I(dstEnumIndex), length); } public static void Copy(EnumArray srcArray, int srcEnumValueIndex, EnumArray dstArray, int dstEnumValueIndex, int length) { srcArray.FillCapacityToLength(); dstArray.FillCapacityToLength(); Array.Copy(srcArray.elements, srcEnumValueIndex - StaticMinInt, dstArray.elements, dstEnumValueIndex - StaticMinInt, length); } public override void FillCapacityToLength() { if (elements == null) { elements = new TValue[StaticLength]; return; } var len = StaticLength; if (elements.Length < len) { Array.Resize(ref elements, len); } } public override void TrimCapacityToLength() { if (elements == null) { return; } var len = StaticLength; if (elements.Length > len) { Array.Resize(ref elements, len); } } } }