Files
xyou f70b061d1a
CI / Go Backend (push) Canceled after 0s
feat: 项目初始化 + 3D方块世界原型 + AI助搭系统
初始化 monorepo: Go后端(7微服务) + Unity客户端(9模块) + 启动器

HTML5原型: Three.js 3D体素世界, Perlin噪声地形, 原版材质, 22种方块

Minecraft创造模式背包: 双栏布局, 拖拽移动物品, 方向性元件引脚

AI助搭策划文档 + 客户端/服务端骨架 + Docker Compose + CI
2026-08-08 14:07:56 +08:00

169 lines
5.7 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
namespace PCL.Core.Utils.Diagnostics;
public static class StackHelper
{
private const string Unknown = "<unknown>";
// 返回“直接调用者”的可读名称(例如 Namespace.Type.Method(paramTypes)
// includeNamespace: 是否包含命名空间
// includeParameters: 是否包含参数类型列表
// skipAppFrames: 额外跳过的应用层帧数量(例如你自己的日志包装器)
[MethodImpl(MethodImplOptions.NoInlining)]
public static string GetDirectCallerName(
bool includeNamespace = true,
bool includeParameters = false,
int skipAppFrames = 0)
{
var st = new StackTrace(skipFrames: 1, fNeedFileInfo: false);
var frame = st.GetFrame(skipAppFrames);
var method = frame?.GetMethod();
if (method is null) return Unknown;
method = _TryMapAsyncOrIterator(method);
return _FormatMethod(method, includeNamespace, includeParameters);
}
// 获取前 maxFrames 层调用栈,返回格式化后的每一帧字符串
// needFileInfo=true 时会解析 PDB 以拿到文件名与行号(昂贵)
[MethodImpl(MethodImplOptions.NoInlining)]
public static IReadOnlyList<string> GetStack(
int maxFrames = 10,
bool includeNamespace = true,
bool includeParameters = false,
bool needFileInfo = false)
{
if (maxFrames <= 0) return [];
var st = new StackTrace(skipFrames: 1, fNeedFileInfo: needFileInfo);
var list = new List<string>(capacity: Math.Min(maxFrames, st.FrameCount));
for (int i = 0, added = 0; i < st.FrameCount && added < maxFrames; i++)
{
var method = st.GetFrame(i)?.GetMethod();
if (method is null) continue;
method = _TryMapAsyncOrIterator(method);
var sig = _FormatMethod(method, includeNamespace, includeParameters);
if (needFileInfo)
{
var f = st.GetFrame(i);
var file = f?.GetFileName();
var line = f?.GetFileLineNumber() ?? 0;
if (!string.IsNullOrEmpty(file) && line > 0)
{
sig = $"{sig} ({System.IO.Path.GetFileName(file)}:{line})";
}
}
list.Add(sig);
added++;
}
return list;
}
// 将 async/iterator 的 MoveNext 映射回原始方法名(尽力而为的启发式)
private static MethodBase _TryMapAsyncOrIterator(MethodBase method)
{
if (method.Name != "MoveNext") return method;
var dt = method.DeclaringType;
if (dt is null) return method;
// 典型生成类型名:<MethodName>d__12 或 <MethodName>g__Local|12_0
var name = dt.Name;
var lt = name.IndexOf('<');
var gt = name.IndexOf('>');
if (lt >= 0 && gt > lt + 1)
{
var originalName = name.Substring(lt + 1, gt - lt - 1);
// 在声明类型的外层类型里查找同名方法(可能存在重载,选第一个匹配)
var parent = dt.DeclaringType ?? dt; // 迭代器常为嵌套到原类型里
foreach (var m in parent.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic))
{
if (string.Equals(m.Name, originalName, StringComparison.Ordinal))
return m;
}
}
return method;
}
private static string _FormatMethod(MethodBase method, bool includeNamespace, bool includeParameters, bool includeParameterNamespace = false)
{
var sb = new StringBuilder();
var type = method.DeclaringType;
if (type is not null)
{
sb.Append(_FormatTypeName(type, includeNamespace));
sb.Append('.');
}
sb.Append(method.Name);
if (method is MethodInfo { IsGenericMethod: true } mi)
{
sb.Append('[');
var args = mi.GetGenericArguments();
for (var i = 0; i < args.Length; i++)
{
if (i > 0) sb.Append(", ");
sb.Append(_FormatTypeName(args[i], false));
}
sb.Append(']');
}
if (includeParameters)
{
var ps = method.GetParameters();
sb.Append('(');
for (var i = 0; i < ps.Length; i++)
{
if (i > 0) sb.Append(", ");
sb.Append(_FormatTypeName(ps[i].ParameterType, includeParameterNamespace));
if (!string.IsNullOrEmpty(ps[i].Name))
{
sb.Append(' ').Append(ps[i].Name);
}
}
sb.Append(')');
}
return sb.ToString();
}
private static string _FormatTypeName(Type t, bool includeNamespace)
{
if (t.IsGenericType)
{
var def = t.GetGenericTypeDefinition();
var name = def.Name;
var tick = name.IndexOf('`');
if (tick >= 0) name = name[..tick];
var ns = includeNamespace ? (def.Namespace is null ? "" : def.Namespace + ".") : "";
var sb = new StringBuilder(ns).Append(name).Append('[');
var args = t.GetGenericArguments();
for (var i = 0; i < args.Length; i++)
{
if (i > 0) sb.Append(", ");
sb.Append(_FormatTypeName(args[i], false));
}
sb.Append(']');
return sb.ToString();
}
return includeNamespace && t.Namespace is not null
? t.Namespace + "." + t.Name
: t.Name;
}
}