Code/Util/SerializableType.cs
namespace Nodebox.Util;
public class SerializableType : IHotloadManaged {
public SerializableType() { }
public SerializableType(Type type) {
Type = type;
}
public static implicit operator SerializableType(Type type) {
if (_convertCache.TryGetValue(type, out var cached)) {
return cached;
}
var result = new SerializableType(type);
_convertCache[type] = result;
return result;
}
public static implicit operator Type(SerializableType type) => type.Type;
[SkipHotload]
private readonly static Dictionary<int, Type> _cache = [];
[SkipHotload]
private readonly static Dictionary<Type, SerializableType> _convertCache = [];
[OnHotload]
private static void InvalidateCache() {
_cache.Clear();
_convertCache.Clear();
}
[JsonIgnore]
[Property]
[Generic]
[Title("Type")]
public Type GenericTypeDefinition {
get {
if (!Identity.HasValue) { return null; }
return TypeLibrary.GetTypeByIdent(Identity.Value).TargetType;
}
set {
if (value == null) {
Identity = null;
return;
}
Identity = TypeLibrary.GetType(value).Identity;
}
}
[JsonIgnore]
[Generic]
[Hide]
public Type Type {
get {
var key = GetHashCode();
if (_cache.TryGetValue(key, out var cached)) {
return cached;
}
Type Get() {
if (!Identity.HasValue) { return null; }
var type = TypeLibrary.GetTypeByIdent(Identity.Value);
if (!IsGeneric || GenericArguments == null || GenericArguments.Length == 0) {
return type.TargetType;
}
if (GenericArguments.Length != type.GenericArguments.Length) {
return null;
}
var inargs = CollectGenericArguments();
if (inargs == null) {
return type.TargetType;
}
try {
return type.MakeGenericType(inargs);
}
catch (Exception e) {
Log.Warning(e);
return null;
}
}
var result = Get();
_cache[key] = result;
return result;
}
set {
if (value == null) {
Identity = null;
GenericArguments = null;
return;
}
Identity = TypeLibrary.GetType(value).Identity;
if (!value.IsGenericType) {
GenericArguments = null;
return;
}
if (TypeLibrary.IsGenericTypeDefinition(value)) {
GenericArguments = new SerializableType[TypeLibrary.GetType(value).GenericArguments.Length];
return;
}
var inargs = TypeLibrary.GetGenericArguments(value);
GenericArguments = new SerializableType[inargs.Length];
for (int i = 0; i < GenericArguments.Length; i++) {
GenericArguments[i] = new SerializableType(inargs[i]);
}
}
}
[JsonInclude]
[ReadOnly]
public int? Identity { get; set; } = null;
[JsonIgnore]
public bool IsGeneric => Identity.HasValue && TypeLibrary.GetTypeByIdent(Identity.Value).IsGenericType;
[JsonInclude]
[Property]
[ShowIf(nameof(IsGeneric), true)]
public SerializableType[] GenericArguments { get; set; } = null;
public T Create<T>(object[] args = null) {
var typeDesc = TypeLibrary.GetType(Type);
if (!typeDesc.IsGenericType) {
return typeDesc.Create<T>(args);
}
var inargs = CollectGenericArguments();
if (inargs == null) { return default; }
return typeDesc.CreateGeneric<T>(inargs, args);
}
protected Type[] CollectGenericArguments() {
if (GenericArguments == null) { return []; }
var inargs = new Type[GenericArguments.Length];
for (int i = 0; i < GenericArguments.Length; i++) {
if (GenericArguments[i] == null) {
return null;
}
if (GenericArguments[i].Type == null) {
return null;
}
inargs[i] = GenericArguments[i].Type;
}
return inargs;
}
public override int GetHashCode() {
var hashCode = HashCode.Combine(Identity);
if (GenericArguments != null) {
foreach (var argument in GenericArguments) {
hashCode = HashCode.Combine(
hashCode,
argument?.GetHashCode()
);
}
}
return hashCode;
}
public override string ToString() => Type?.ToSimpleString() ?? "SerializableType(null)";
}