3 using System.Collections.Concurrent;
4 using System.Collections.Generic;
5 using System.Collections.Immutable;
7 using System.Reflection;
11 public static class ReflectionUtils
13 private static readonly ConcurrentDictionary<Assembly, ImmutableArray<Type>> CachedNonAbstractTypes =
new();
14 private static readonly ConcurrentDictionary<string, ImmutableArray<Type>> TypeSearchCache =
new();
16 public static IEnumerable<Type> GetDerivedNonAbstract<T>()
19 string typeName = t.FullName ?? t.Name;
22 if (TypeSearchCache.TryGetValue(typeName, out var value))
28 Assembly assembly = typeof(T).Assembly;
29 if (!CachedNonAbstractTypes.ContainsKey(assembly))
31 AddNonAbstractAssemblyTypes(assembly);
35 var list = CachedNonAbstractTypes.Values
36 .SelectMany(arr => arr.Where(type => type.IsSubclassOf(t)))
41 return ImmutableArray<Type>.Empty;
44 if (!TypeSearchCache.TryAdd(typeName, list))
46 DebugConsole.LogError($
"ReflectionUtils::AddNonAbstractAssemblyTypes() | Error while adding to quick lookup cache.");
56 public static void AddNonAbstractAssemblyTypes(Assembly assembly,
bool overwrite =
false)
58 if (CachedNonAbstractTypes.ContainsKey(assembly))
62 DebugConsole.LogError(
63 $
"ReflectionUtils::AddNonAbstractAssemblyTypes() | The assembly [{assembly.GetName()}] already exists in the cache.");
67 CachedNonAbstractTypes.Remove(assembly, out _);
72 if (!CachedNonAbstractTypes.TryAdd(assembly, assembly.GetSafeTypes().Where(t => !t.IsAbstract).ToImmutableArray()))
74 DebugConsole.LogError($
"ReflectionUtils::AddNonAbstractAssemblyTypes() | Unable to add types from Assembly to cache.");
78 TypeSearchCache.Clear();
81 catch (ReflectionTypeLoadException e)
83 DebugConsole.LogError($
"ReflectionUtils::AddNonAbstractAssemblyTypes() | RTFException: Unable to load Assembly Types from {assembly.GetName()}.");
91 public static void RemoveAssemblyFromCache(Assembly assembly)
93 CachedNonAbstractTypes.Remove(assembly, out _);
94 TypeSearchCache.Clear();
100 internal static void ResetCache()
102 CachedNonAbstractTypes.Clear();
103 CachedNonAbstractTypes.TryAdd(typeof(ReflectionUtils).Assembly, typeof(ReflectionUtils).Assembly.GetSafeTypes().ToImmutableArray());
104 TypeSearchCache.Clear();
107 public static Option<TBase> ParseDerived<TBase, TInput>(TInput input) where TInput : notnull where TBase : notnull
109 static Option<TBase> none() => Option<TBase>.None();
111 var derivedTypes = GetDerivedNonAbstract<TBase>();
113 Option<TBase> parseOfType(Type t)
117 var parseFunc = t.GetMethod(
"Parse", BindingFlags.Public | BindingFlags.Static);
118 if (parseFunc is
null) {
return none(); }
120 var parameters = parseFunc.GetParameters();
121 if (parameters.Length != 1) {
return none(); }
123 var returnType = parseFunc.ReturnType;
124 if (!returnType.IsConstructedGenericType) {
return none(); }
125 if (returnType.GetGenericTypeDefinition() != typeof(Option<>)) {
return none(); }
126 if (returnType.GenericTypeArguments[0] != t) {
return none(); }
129 static Option<TBase> convert<T2>(Option<T2> option) where T2 : TBase
130 => option.Select(v => (TBase)v);
131 Func<Option<TBase>, Option<TBase>> f = convert;
132 var genericArgs = f.Method.GetGenericArguments();
134 var constructedConverter =
135 f.Method.GetGenericMethodDefinition().MakeGenericMethod(genericArgs);
137 return constructedConverter.Invoke(
null,
new[] { parseFunc.Invoke(
null,
new object[] { input }) })
138 as Option<TBase>? ?? none();
141 return derivedTypes.Select(parseOfType).FirstOrDefault(t => t.IsSome());
144 public static string NameWithGenerics(
this Type t)
146 if (!t.IsGenericType) {
return t.Name; }
148 string result = t.Name[..t.Name.IndexOf(
'`')];
149 result += $
"<{string.Join(",
", t.GetGenericArguments().Select(NameWithGenerics))}>";