GetAllChildClasses: the type memo is concurrent

A plain Dictionary written by parallel catalog loads corrupted itself ("Operations that change non-concurrent collections must have exclusive
access") and failed unrelated tests at random, through ValidateStatModifiers.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Nibu 2026-11-11 05:07:47 +00:00
commit 85f1a933d4

View file

@ -32,16 +32,13 @@ public static class Extensions
// Every loadable type assignable to this one. An assembly whose dependencies are missing (a Unity plugin
// referencing UnityEngine outside Unity) still yields the types that do load.
private static readonly Dictionary<Type, Type[]> ChildClasses = new Dictionary<Type, Type[]>();
public static Type[] GetAllChildClasses(this Type type)
// Read from parallel loaders (catalog validation, parallel tests), so the memo is concurrent.
private static readonly System.Collections.Concurrent.ConcurrentDictionary<Type, Type[]> ChildClasses = new System.Collections.Concurrent.ConcurrentDictionary<Type, Type[]>();
public static Type[] GetAllChildClasses(this Type type) => ChildClasses.GetOrAdd(type, t => AppDomain.CurrentDomain.GetAssemblies().SelectMany(assembly =>
{
if (ChildClasses.TryGetValue(type, out var children)) return children;
return ChildClasses[type] = AppDomain.CurrentDomain.GetAssemblies().SelectMany(assembly =>
{
try { return assembly.GetTypes(); }
catch (ReflectionTypeLoadException e) { return e.Types.Where(t => t != null); }
}).Where(type.IsAssignableFrom).ToArray();
}
try { return assembly.GetTypes(); }
catch (ReflectionTypeLoadException e) { return e.Types.Where(x => x != null); }
}).Where(t.IsAssignableFrom).ToArray());
public static string SplitCamelCase(this string str) =>
Regex.Replace(Regex.Replace(str, @"(\P{Ll})(\P{Ll}\p{Ll})", "$1 $2"), @"(\p{Ll})(\P{Ll})", "$1 $2");