DragonECS/src/Debug/EcsDebugUtility.cs

485 lines
17 KiB
C#
Raw Normal View History

2024-02-29 02:37:04 +08:00
using System;
2023-06-16 11:53:59 +08:00
using System.Collections.Generic;
using System.Linq;
2023-05-23 01:47:28 +08:00
using System.Reflection;
using System.Runtime.CompilerServices;
2023-05-23 01:47:28 +08:00
namespace DCFApixels.DragonECS
{
public static class EcsDebugUtility
{
private const BindingFlags RFL_FLAGS = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
2023-06-16 12:34:54 +08:00
#region GetGenericTypeName
2023-05-26 04:25:09 +08:00
public static string GetGenericTypeFullName<T>(int maxDepth = 2) => GetGenericTypeFullName(typeof(T), maxDepth);
public static string GetGenericTypeFullName(Type type, int maxDepth = 2) => GetGenericTypeNameInternal(type, maxDepth, true);
2023-05-23 01:47:28 +08:00
public static string GetGenericTypeName<T>(int maxDepth = 2) => GetGenericTypeName(typeof(T), maxDepth);
2023-05-26 04:25:09 +08:00
public static string GetGenericTypeName(Type type, int maxDepth = 2) => GetGenericTypeNameInternal(type, maxDepth, false);
private static string GetGenericTypeNameInternal(Type type, int maxDepth, bool isFull)
2023-05-23 01:47:28 +08:00
{
#if (DEBUG && !DISABLE_DEBUG)
2023-06-16 12:34:54 +08:00
string result = isFull ? type.FullName : type.Name;
2023-05-23 01:47:28 +08:00
if (!type.IsGenericType || maxDepth == 0)
2023-06-16 12:34:54 +08:00
return result;
2023-05-23 01:47:28 +08:00
2023-06-16 12:34:54 +08:00
int iBacktick = result.IndexOf('`');
2023-05-23 01:47:28 +08:00
if (iBacktick > 0)
2023-06-16 12:34:54 +08:00
result = result.Remove(iBacktick);
2023-05-23 01:47:28 +08:00
2023-06-16 12:34:54 +08:00
result += "<";
2023-05-23 01:47:28 +08:00
Type[] typeParameters = type.GetGenericArguments();
for (int i = 0; i < typeParameters.Length; ++i)
{
2023-05-26 04:25:09 +08:00
string typeParamName = GetGenericTypeNameInternal(typeParameters[i], maxDepth - 1, false);//чтобы строка не была слишком длинной, используются сокращенные имена для типов аргументов
2023-06-16 12:34:54 +08:00
result += (i == 0 ? typeParamName : "," + typeParamName);
2023-05-23 01:47:28 +08:00
}
2023-06-16 12:34:54 +08:00
result += ">";
return result;
2023-05-23 02:11:00 +08:00
#else //optimization for release build
2023-05-23 01:47:28 +08:00
return type.Name;
#endif
}
2023-06-16 12:34:54 +08:00
#endregion
2023-05-23 01:47:28 +08:00
2023-06-22 10:50:47 +08:00
#region AutoToString
2023-06-22 10:58:57 +08:00
/// <summary> slow but automatic conversion of ValueType to string in the format "name(field1, field2... fieldn)" </summary>
2023-06-22 10:50:47 +08:00
public static string AutoToString<T>(this T self, bool isWriteName = true) where T : struct
{
return AutoToString(self, typeof(T), isWriteName);
}
2023-06-22 10:58:57 +08:00
private static string AutoToString(object target, Type type, bool isWriteName)
2023-06-22 10:50:47 +08:00
{
#if !REFLECTION_DISABLED
2024-01-18 21:25:26 +08:00
//TODO сделать специальный вывод в виде названий констант для Enum-ов
#pragma warning disable IL2070 // 'this' argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to target method. The parameter of method does not have matching annotations.
var fields = type.GetFields(RFL_FLAGS);
#pragma warning restore IL2070
2023-06-22 10:50:47 +08:00
string[] values = new string[fields.Length];
for (int i = 0; i < fields.Length; i++)
{
2023-06-22 11:00:33 +08:00
values[i] = (fields[i].GetValue(target) ?? "NULL").ToString();
}
2023-06-22 10:58:57 +08:00
if (isWriteName)
{
2023-06-22 10:50:47 +08:00
return $"{type.Name}({string.Join(", ", values)})";
}
2023-06-22 10:50:47 +08:00
else
{
2023-06-22 10:50:47 +08:00
return $"({string.Join(", ", values)})";
}
2024-02-26 10:43:37 +08:00
#else
EcsDebug.PrintWarning($"Reflection is not available, the {nameof(AutoToString)} method does not work.");
return string.Empty;
2024-02-26 10:43:37 +08:00
#endif
2023-06-22 10:50:47 +08:00
}
#endregion
2023-06-16 12:34:54 +08:00
#region GetName
public static string GetName(object obj, int maxGenericDepth = 2)
{
return obj is IEcsMetaProvider intr ?
GetName(intr.MetaSource, maxGenericDepth) :
GetName(type: obj.GetType(), maxGenericDepth);
}
public static string GetName<T>(int maxGenericDepth = 2) => GetName(typeof(T), maxGenericDepth);
public static string GetName(Type type, int maxGenericDepth = 2) => type.TryGetCustomAttribute(out MetaNameAttribute atr) ? atr.name : GetGenericTypeName(type, maxGenericDepth);
public static bool TryGetCustomName(object obj, out string name)
{
return obj is IEcsMetaProvider intr ?
TryGetCustomName(intr.MetaSource, out name) :
TryGetCustomName(type: obj.GetType(), out name);
}
public static bool TryGetCustomName<T>(out string name) => TryGetCustomName(type: typeof(T), out name);
2023-06-16 11:53:59 +08:00
public static bool TryGetCustomName(Type type, out string name)
{
if (type.TryGetCustomAttribute(out MetaNameAttribute atr))
2023-06-16 11:53:59 +08:00
{
name = atr.name;
return true;
}
name = string.Empty;
return false;
}
2023-06-16 12:34:54 +08:00
#endregion
2023-05-23 01:47:28 +08:00
#region GetGroup
public static MetaGroup GetGroup(object obj)
{
return obj is IEcsMetaProvider intr ?
GetGroup(intr.MetaSource) :
GetGroup(type: obj.GetType());
}
public static MetaGroup GetGroup<T>() => GetGroup(typeof(T));
public static MetaGroup GetGroup(Type type) => type.TryGetCustomAttribute(out MetaGroupAttribute atr) ? atr.GetData() : MetaGroup.Empty;
public static bool TryGetGroup(object obj, out MetaGroup group)
{
return obj is IEcsMetaProvider intr ?
TryGetGroup(intr.MetaSource, out group) :
TryGetGroup(type: obj.GetType(), out group);
}
public static bool TryGetGroup<T>(out MetaGroup text) => TryGetGroup(typeof(T), out text);
public static bool TryGetGroup(Type type, out MetaGroup group)
{
if (type.TryGetCustomAttribute(out MetaGroupAttribute atr))
{
group = atr.GetData();
return true;
}
group = MetaGroup.Empty;
return false;
}
#endregion
2023-06-16 12:34:54 +08:00
#region GetDescription
public static string GetDescription(object obj)
{
return obj is IEcsMetaProvider intr ?
GetDescription(intr.MetaSource) :
GetDescription(type: obj.GetType());
}
2023-05-23 01:47:28 +08:00
public static string GetDescription<T>() => GetDescription(typeof(T));
public static string GetDescription(Type type) => type.TryGetCustomAttribute(out MetaDescriptionAttribute atr) ? atr.description : string.Empty;
public static bool TryGetDescription(object obj, out string text)
{
return obj is IEcsMetaProvider intr ?
TryGetDescription(intr.MetaSource, out text) :
TryGetDescription(type: obj.GetType(), out text);
}
2023-06-16 11:53:59 +08:00
public static bool TryGetDescription<T>(out string text) => TryGetDescription(typeof(T), out text);
public static bool TryGetDescription(Type type, out string text)
{
if (type.TryGetCustomAttribute(out MetaDescriptionAttribute atr))
2023-06-16 11:53:59 +08:00
{
text = atr.description;
return true;
}
text = string.Empty;
return false;
}
2023-06-16 12:34:54 +08:00
#endregion
2023-06-16 11:53:59 +08:00
#region GetColor
public static MetaColor GetColor(object obj)
{
return obj is IEcsMetaProvider intr ?
GetColor(intr.MetaSource) :
GetColor(type: obj.GetType());
}
public static MetaColor GetColor<T>()
{
return GetColor(typeof(T));
}
public static MetaColor GetColor(Type type)
2023-05-23 01:47:28 +08:00
{
var atr = type.GetCustomAttribute<MetaColorAttribute>();
2024-03-03 00:28:16 +08:00
return atr != null ? atr.color : AutoColor(type);
}
private static MetaColor AutoColor(Type type)
{
2024-03-03 00:39:32 +08:00
return new MetaColor(type.Name).Desaturate(0.75f) / 1.15f;
2023-06-16 11:53:59 +08:00
}
public static bool TryGetColor(object obj, out MetaColor color)
{
return obj is IEcsMetaProvider intr ?
TryGetColor(intr.MetaSource, out color) :
TryGetColor(type: obj.GetType(), out color);
}
public static bool TryGetColor<T>(out MetaColor color)
{
return TryGetColor(typeof(T), out color);
}
public static bool TryGetColor(Type type, out MetaColor color)
2023-06-16 11:53:59 +08:00
{
var atr = type.GetCustomAttribute<MetaColorAttribute>();
2023-06-22 14:39:12 +08:00
if (atr != null)
2023-06-16 11:53:59 +08:00
{
color = atr.color;
2023-06-16 11:53:59 +08:00
return true;
}
color = MetaColor.BlackColor;
return false;
}
#endregion
#region GetTags
public static IReadOnlyCollection<string> GetTags(object obj)
{
return obj is IEcsMetaProvider intr ?
GetTags(intr.MetaSource) :
GetTags(type: obj.GetType());
}
public static IReadOnlyCollection<string> GetTags<T>()
{
return GetTags(typeof(T));
}
public static IReadOnlyCollection<string> GetTags(Type type)
{
var atr = type.GetCustomAttribute<MetaTagsAttribute>();
return atr != null ? atr.Tags : Array.Empty<string>();
}
public static bool TryGetTags(object obj, out IReadOnlyCollection<string> tags)
{
return obj is IEcsMetaProvider intr ?
TryGetTags(intr.MetaSource, out tags) :
TryGetTags(type: obj.GetType(), out tags);
}
public static bool TryGetTags<T>(out IReadOnlyCollection<string> tags)
{
return TryGetTags(typeof(T), out tags);
}
public static bool TryGetTags(Type type, out IReadOnlyCollection<string> tags)
{
var atr = type.GetCustomAttribute<MetaTagsAttribute>();
if (atr != null)
{
tags = atr.Tags;
return true;
}
tags = Array.Empty<string>();
2023-06-16 11:53:59 +08:00
return false;
2023-05-23 01:47:28 +08:00
}
2023-06-16 11:53:59 +08:00
#endregion
2023-05-31 04:09:55 +08:00
2023-06-16 12:34:54 +08:00
#region IsHidden
public static bool IsHidden(object obj)
{
return obj is IEcsMetaProvider intr ?
IsHidden(intr.MetaSource) :
IsHidden(type: obj.GetType());
}
public static bool IsHidden<T>()
{
return IsHidden(typeof(T));
}
public static bool IsHidden(Type type)
{
return type.TryGetCustomAttribute(out MetaTagsAttribute atr) && atr.Tags.Contains(MetaTags.HIDDEN);
}
2023-06-16 12:34:54 +08:00
#endregion
#region MetaSource
public static bool IsMetaSourceProvided(object obj)
{
return obj is IEcsMetaProvider;
}
public static object GetMetaSource(object obj)
{
return obj is IEcsMetaProvider intr ? intr.MetaSource : obj;
}
#endregion
#region GenerateTypeMeta
public static TypeMetaData GenerateTypeMeta(object obj)
{
return obj is IEcsMetaProvider intr ?
GenerateTypeMeta(intr.MetaSource) :
GenerateTypeMeta(type: obj.GetType());
}
public static TypeMetaData GenerateTypeMeta<T>()
{
return GenerateTypeMeta(typeof(T));
}
public static TypeMetaData GenerateTypeMeta(Type type)
{
return new TypeMetaData(
type,
GetName(type),
GetGroup(type),
GetColor(type),
GetDescription(type),
GetTags(type));
}
#endregion
#region GetCachedTypeMeta
private static readonly Dictionary<Type, TypeMetaDataCached> _metaCache = new Dictionary<Type, TypeMetaDataCached>();
public static TypeMetaDataCached GetCachedTypeMeta(object obj)
{
return obj is IEcsMetaProvider intr ?
GetCachedTypeMeta(intr.MetaSource) :
GetCachedTypeMeta(type: obj.GetType());
}
public static TypeMetaDataCached GetCachedTypeMeta<T>()
{
return GetCachedTypeMeta(typeof(T));
}
public static TypeMetaDataCached GetCachedTypeMeta(Type type)
{
2024-02-26 11:58:19 +08:00
if (_metaCache.TryGetValue(type, out TypeMetaDataCached result) == false)
{
result = new TypeMetaDataCached(type);
_metaCache.Add(type, result);
}
return result;
}
#endregion
2023-06-16 12:34:54 +08:00
#region ReflectionExtensions
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2023-12-31 23:26:56 +08:00
public static bool TryGetCustomAttribute<T>(this Type self, out T attribute) where T : Attribute
2023-05-31 04:09:55 +08:00
{
2023-06-16 12:34:54 +08:00
attribute = self.GetCustomAttribute<T>();
return attribute != null;
2023-05-31 04:09:55 +08:00
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2023-12-31 23:26:56 +08:00
public static bool TryGetCustomAttribute<T>(this MemberInfo self, out T attribute) where T : Attribute
2023-06-16 12:34:54 +08:00
{
attribute = self.GetCustomAttribute<T>();
return attribute != null;
}
#endregion
2023-05-23 01:47:28 +08:00
}
[Serializable]
public sealed class TypeMetaData
{
public readonly Type type;
public readonly string name;
public readonly MetaGroup group;
public readonly MetaColor color;
public readonly string description;
public readonly IReadOnlyCollection<string> tags;
public TypeMetaData(Type type, string name, MetaGroup group, MetaColor color, string description, IReadOnlyCollection<string> tags)
{
this.type = type;
this.name = name;
this.group = group;
this.color = color;
this.description = description;
2024-02-26 11:58:19 +08:00
this.tags = tags;
}
}
2024-02-29 02:37:04 +08:00
public static class TypeMetaDataCachedExtensions
{
public static TypeMetaDataCached GetMeta(this object self)
{
return EcsDebugUtility.GetCachedTypeMeta(self);
}
}
public class TypeMetaDataCached
{
internal readonly Type _type;
internal string _name;
internal MetaGroup _group;
2024-02-29 02:37:04 +08:00
internal MetaColor _color;
internal string _description;
internal IReadOnlyCollection<string> _tags;
private TypeMetaData _typeMetaData = null;
private InitFlag _initFlags = InitFlag.None;
public TypeMetaDataCached(Type type)
{
_type = type;
}
public Type Type
{
get { return _type; }
}
public string Name
{
get
{
2024-02-29 02:37:04 +08:00
if (_initFlags.HasFlag(InitFlag.Name) == false)
{
_name = EcsDebugUtility.GetName(_type);
_initFlags |= InitFlag.Name;
}
return _name;
}
}
public MetaGroup Group
{
get
{
2024-02-29 02:37:04 +08:00
if (_initFlags.HasFlag(InitFlag.Group) == false)
{
_group = EcsDebugUtility.GetGroup(_type);
_initFlags |= InitFlag.Group;
}
return _group;
}
}
public MetaColor Color
{
get
{
2024-02-29 02:37:04 +08:00
if (_initFlags.HasFlag(InitFlag.Color) == false)
{
_color = EcsDebugUtility.GetColor(_type);
2024-03-02 22:13:44 +08:00
_initFlags |= InitFlag.Color;
}
2024-02-29 02:37:04 +08:00
return _color;
}
}
public string Description
{
get
{
2024-02-29 02:37:04 +08:00
if (_initFlags.HasFlag(InitFlag.Description) == false)
{
_description = EcsDebugUtility.GetDescription(_type);
_initFlags |= InitFlag.Description;
}
return _description;
}
}
public IReadOnlyCollection<string> Tags
{
get
{
2024-02-29 02:37:04 +08:00
if (_initFlags.HasFlag(InitFlag.Tags) == false)
{
_tags = EcsDebugUtility.GetTags(_type);
_initFlags |= InitFlag.Tags;
}
return _tags;
}
}
public TypeMetaData AllData
{
get
{
2024-02-26 11:58:19 +08:00
if (_typeMetaData == null)
{
_typeMetaData = new TypeMetaData(
Type,
Name,
Group,
Color,
Description,
Tags);
}
return _typeMetaData;
}
}
public void InitializeAll()
{
if (_initFlags == InitFlag.All)
{
return;
}
_ = Name;
_ = Group;
_ = Color;
_ = Description;
_ = Tags;
}
private enum InitFlag : byte
{
None = 0,
Name = 1 << 0,
Group = 1 << 1,
Color = 1 << 2,
Description = 1 << 3,
Tags = 1 << 4,
All = Name | Group | Color | Description | Tags
}
}
}