素直な static type caching 技法になるように書き直し

This commit is contained in:
ousttrue 2022-10-14 15:20:05 +09:00
parent 75e9a82ee8
commit d540b04cc4
3 changed files with 42 additions and 33 deletions

View File

@ -2,11 +2,22 @@
namespace UniGLTF.Utils
{
/// <summary>
/// CachedEnumType<T> に対するインターフェース。
/// 非 Generic class
/// </summary>
public static class CachedEnum
{
public static T Parse<T>(string name, bool ignoreCase = false) where T : struct, Enum
{
return CachedEnumType<T>.Parse(name, ignoreCase);
if (ignoreCase)
{
return CachedEnumType<T>.IgnoreCaseMap[name];
}
else
{
return CachedEnumType<T>.Map[name];
}
}
public static T TryParseOrDefault<T>(string name, bool ignoreCase = false, T defaultValue = default)
@ -16,7 +27,7 @@ namespace UniGLTF.Utils
{
return Parse<T>(name, ignoreCase: ignoreCase);
}
catch
catch (System.Collections.Generic.KeyNotFoundException)
{
return defaultValue;
}

View File

@ -3,41 +3,37 @@ using System.Collections.Generic;
namespace UniGLTF.Utils
{
/// <summary>
/// enum T に対する static type caching 。
///
/// CachedEnumType<T>.Values
/// CachedEnumType<T>.Map
/// CachedEnumType<T>.IgnoreCaseMap
///
/// がスレッドセーフに(キャッシュされた)同じ値を返す。
/// </summary>
internal static class CachedEnumType<T> where T : struct, Enum
{
private static readonly Dictionary<string, T> _values = new Dictionary<string, T>();
private static readonly Dictionary<string, T> _ignoreCaseValues = new Dictionary<string, T>(StringComparer.OrdinalIgnoreCase);
private static T[] _allValues;
public readonly static Dictionary<string, T> Map = CreateStringEnumMap(false);
public readonly static Dictionary<string, T> IgnoreCaseMap = CreateStringEnumMap(true);
public readonly static T[] Values = (T[])Enum.GetValues(typeof(T));
public static T[] Values
private static Dictionary<string, T> CreateStringEnumMap(bool ignoreCase)
{
get
var dict = ignoreCase
? new Dictionary<string, T>(StringComparer.OrdinalIgnoreCase)
: new Dictionary<string, T>()
;
// ここで Values を使うと
// System.TypeInitializationException
// が起きる。
// static 変数初期化中に別の static 変数を参照すると未初期化がありえるぽい(初期化順?)
foreach (T value in Enum.GetValues(typeof(T)))
{
if (_allValues == null)
{
_allValues = Enum.GetValues(typeof(T)) as T[];
}
return _allValues;
dict.Add(value.ToString(), value);
}
}
public static T Parse(string name, bool ignoreCase)
{
var caches = ignoreCase ? _ignoreCaseValues : _values;
if (caches.TryGetValue(name, out var ignoreCaseValue))
{
return ignoreCaseValue;
}
if (Enum.TryParse<T>(name, ignoreCase, out var result))
{
caches.Add(name, result);
return result;
}
throw new ArgumentException(name);
return dict;
}
}
}
}

View File

@ -1,4 +1,5 @@
using NUnit.Framework;
using System.Linq;
using NUnit.Framework;
using UniGLTF.Utils;
using UnityEngine;
@ -12,6 +13,7 @@ namespace UniGLTF
{
Assert.AreEqual(default(HumanBodyBones), CachedEnum.TryParseOrDefault<HumanBodyBones>("xxx"));
Assert.AreEqual(HumanBodyBones.UpperChest, CachedEnum.TryParseOrDefault<HumanBodyBones>("upperchest", true));
Assert.AreEqual(CachedEnum.GetValues<HumanBodyBones>().First(x => x == HumanBodyBones.Hips), HumanBodyBones.Hips);
}
}
}