Files
ShrinkEventBus/Editor/EventBusViewerWindow.cs
T
cneicy 8acccdcc4b feat: 重构事件总线为可实例化架构并发布 1.3.0
- 新增 IShrinkEventBus、ShrinkEventBusBuilder 与 ShrinkEventBusInstance,支持独立 bus、异常策略、启动开关、类型校验和按 phase 派发
- 新增 SubscribeEvent 可释放订阅句柄与订阅快照接口,保留 EventBus 静态门面并委托到默认实例
- 重构监听列表为父子事件链共享锁与快照缓存,支持父事件监听子事件、跨父子数字优先级归并和防御性快照拷贝
- 优化 EventBase 元数据缓存、懒生成 EventId、派发快照挂接与对象池双重归还保护,移除旧 EventCache 热路径
- 恢复本地 ILPostProcessor 兜底并生成静态订阅注册表,避免共享 CodeGen 覆盖外程序集自动注册失效
- 改进事件查看器、Benchmark、README 与 CHANGELOG,并新增 Dispatch/Priority/Registration/Subscription 测试覆盖核心行为

BREAKING CHANGE: 移除 EventBase.GetListenerList(),int 数字优先级重载不再提供默认值,数字优先级 0 现在映射为 NORMAL。
2026-06-18 17:05:16 +08:00

794 lines
31 KiB
C#
Raw 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.
#if UNITY_EDITOR
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;
namespace ShrinkEventBus.Editor
{
public class EventBusViewerWindow : EditorWindow
{
private enum LiveLogFilterMode
{
All,
HasListeners,
NoListeners
}
private sealed class HandlerDisplayData
{
public object TargetInstance { get; set; }
public MonoBehaviour PingTarget { get; set; }
public string TargetName { get; set; }
public string MethodName { get; set; }
public object Priority { get; set; }
public string ExecType { get; set; }
public bool IsStaticHandler { get; set; }
}
private sealed class EventLogRecord
{
public DateTime Timestamp { get; set; }
public string TimestampText { get; set; }
public string EventName { get; set; }
public string SenderInfo { get; set; }
public bool HasListeners { get; set; }
public bool IsCancelable { get; set; }
public bool IsCanceled { get; set; }
public string Result { get; set; }
public string Phase { get; set; }
public List<string> Parameters { get; set; }
public List<HandlerDisplayData> Handlers { get; set; }
public string MatchText { get; set; }
}
private sealed class EventLogGroup
{
public string EventName { get; set; }
public List<EventLogRecord> Records { get; } = new();
public EventLogRecord LatestRecord => Records[0];
}
private ScrollView _subscribersScrollView;
private ScrollView _liveTrackerScrollView;
private VisualElement _subscribersTab;
private VisualElement _liveTrackerTab;
private Label _instanceCountLabel;
private Label _liveLogStatsLabel;
private string _searchString = "";
private string _liveLogSearchString = "";
private LiveLogFilterMode _liveLogFilterMode = LiveLogFilterMode.All;
private bool _collapseSameEventType = true;
private const int MaxLogCount = 100;
private const double LiveTrackerRefreshIntervalSeconds = 0.1d;
private readonly List<EventLogRecord> _logRecords = new();
private bool _isLiveTrackerDirty;
private double _nextLiveTrackerRefreshTime;
[MenuItem("ShrinkSDK/事件总线/事件查看器")]
public static void ShowWindow()
{
var window = GetWindow<EventBusViewerWindow>("EventBus 控制台");
window.minSize = new Vector2(500, 600);
window.Show();
}
private void OnEnable()
{
EventBus.OnEventTriggeredForEditor += RecordEventLog;
EditorApplication.update += OnEditorUpdate;
}
private void OnDisable()
{
EventBus.OnEventTriggeredForEditor -= RecordEventLog;
EditorApplication.update -= OnEditorUpdate;
}
private void CreateGUI()
{
var root = rootVisualElement;
root.style.flexDirection = FlexDirection.Column;
root.Add(CreateHeader());
root.Add(CreateTabBar());
var contentContainer = new VisualElement { style = { flexGrow = 1 } };
_subscribersTab = CreateSubscribersTab();
_liveTrackerTab = CreateLiveTrackerTab();
_subscribersTab.style.display = DisplayStyle.Flex;
_liveTrackerTab.style.display = DisplayStyle.None;
contentContainer.Add(_subscribersTab);
contentContainer.Add(_liveTrackerTab);
root.Add(contentContainer);
RefreshSubscribersView();
root.schedule.Execute(() =>
{
_instanceCountLabel.text = Application.isPlaying
? $"活跃实例数: {EventBus.GetRegisteredInstanceCount()}"
: "游戏未运行";
}).Every(500);
}
private VisualElement CreateHeader()
{
var header = new VisualElement
{
style =
{
paddingTop = 10, paddingBottom = 10, paddingLeft = 10, paddingRight = 10,
backgroundColor = new StyleColor(new Color(0.15f, 0.15f, 0.15f, 1f)), borderBottomWidth = 1,
borderBottomColor = new StyleColor(Color.black)
}
};
header.Add(new Label("Shrink Event Bus 控制台")
{ style = { fontSize = 16, unityFontStyleAndWeight = FontStyle.Bold } });
var debugToggle = new Toggle("启用 Debug 调试记录 (追踪实时事件)")
{ value = EventBus.EnableDebugRecord, style = { marginTop = 5 } };
debugToggle.RegisterValueChangedCallback(evt => EventBus.EnableDebugRecord = evt.newValue);
header.Add(debugToggle);
var infoRow = new VisualElement
{ style = { flexDirection = FlexDirection.Row, marginTop = 5, justifyContent = Justify.SpaceBetween } };
_instanceCountLabel = new Label("活跃实例数: 0") { style = { color = new StyleColor(Color.cyan) } };
infoRow.Add(_instanceCountLabel);
header.Add(infoRow);
return header;
}
private VisualElement CreateTabBar()
{
var toolbar = new Toolbar();
toolbar.Add(new ToolbarButton(() => SwitchTab(0))
{ text = "订阅者全览", style = { flexGrow = 1, unityTextAlign = TextAnchor.MiddleCenter } });
toolbar.Add(new ToolbarButton(() => SwitchTab(1))
{ text = "实时触发日志", style = { flexGrow = 1, unityTextAlign = TextAnchor.MiddleCenter } });
return toolbar;
}
private VisualElement CreateSubscribersTab()
{
var container = new VisualElement { style = { flexGrow = 1 } };
var toolbar = new Toolbar();
var searchField = new ToolbarSearchField { style = { flexGrow = 1 } };
searchField.RegisterValueChangedCallback(evt =>
{
_searchString = evt.newValue;
RefreshSubscribersView();
});
toolbar.Add(searchField);
toolbar.Add(new ToolbarButton(RefreshSubscribersView) { text = "刷新视图" });
container.Add(toolbar);
_subscribersScrollView = new ScrollView
{ style = { flexGrow = 1, paddingLeft = 5, paddingRight = 5, paddingTop = 5 } };
container.Add(_subscribersScrollView);
return container;
}
private VisualElement CreateLiveTrackerTab()
{
var container = new VisualElement { style = { flexGrow = 1 } };
var toolbar = new Toolbar();
var searchField = new ToolbarSearchField { style = { flexGrow = 1 } };
searchField.RegisterValueChangedCallback(evt =>
{
_liveLogSearchString = evt.newValue ?? string.Empty;
RequestLiveTrackerRefresh(true);
});
toolbar.Add(searchField);
var filterMenu = new ToolbarMenu { text = "全部事件" };
filterMenu.menu.AppendAction("全部事件", _ =>
{
_liveLogFilterMode = LiveLogFilterMode.All;
filterMenu.text = "全部事件";
RequestLiveTrackerRefresh(true);
});
filterMenu.menu.AppendAction("仅有监听者", _ =>
{
_liveLogFilterMode = LiveLogFilterMode.HasListeners;
filterMenu.text = "仅有监听者";
RequestLiveTrackerRefresh(true);
});
filterMenu.menu.AppendAction("仅无监听者", _ =>
{
_liveLogFilterMode = LiveLogFilterMode.NoListeners;
filterMenu.text = "仅无监听者";
RequestLiveTrackerRefresh(true);
});
toolbar.Add(filterMenu);
var collapseToggle = new ToolbarToggle
{
text = "折叠同类事件",
value = _collapseSameEventType
};
collapseToggle.RegisterValueChangedCallback(evt =>
{
_collapseSameEventType = evt.newValue;
RequestLiveTrackerRefresh(true);
});
toolbar.Add(collapseToggle);
toolbar.Add(new ToolbarButton(ClearLogs) { text = "清空日志" });
container.Add(toolbar);
_liveLogStatsLabel = new Label
{
style =
{
marginLeft = 8,
marginRight = 8,
marginTop = 4,
marginBottom = 4,
color = new StyleColor(Color.gray)
}
};
container.Add(_liveLogStatsLabel);
_liveTrackerScrollView = new ScrollView { style = { flexGrow = 1, paddingLeft = 5, paddingRight = 5 } };
container.Add(_liveTrackerScrollView);
RequestLiveTrackerRefresh(true);
return container;
}
private void SwitchTab(int index)
{
_subscribersTab.style.display = index == 0 ? DisplayStyle.Flex : DisplayStyle.None;
_liveTrackerTab.style.display = index == 1 ? DisplayStyle.Flex : DisplayStyle.None;
if (index == 0)
{
RefreshSubscribersView();
return;
}
RequestLiveTrackerRefresh(true);
}
private void OnEditorUpdate()
{
if (!_isLiveTrackerDirty) return;
if (_liveTrackerTab == null || _liveTrackerTab.style.display != DisplayStyle.Flex) return;
if (EditorApplication.timeSinceStartup < _nextLiveTrackerRefreshTime) return;
RefreshLiveTrackerView();
}
private void RequestLiveTrackerRefresh(bool immediate = false)
{
_isLiveTrackerDirty = true;
if (immediate)
{
_nextLiveTrackerRefreshTime = EditorApplication.timeSinceStartup;
if (_liveTrackerTab != null && _liveTrackerTab.style.display == DisplayStyle.Flex)
RefreshLiveTrackerView();
}
}
private void RefreshSubscribersView()
{
_subscribersScrollView.Clear();
if (!Application.isPlaying)
{
_subscribersScrollView.Add(new HelpBox("请先运行游戏,系统初始化后即可查看内存中的事件树。", HelpBoxMessageType.Info));
return;
}
var snapshots = EventBus.GetAllSubscribersSnapshot();
foreach (var entry in snapshots)
{
var eventType = entry.Key;
if (!string.IsNullOrEmpty(_searchString) &&
!eventType.Name.ToLower().Contains(_searchString.ToLower())) continue;
var handlers = entry.Value;
var foldout = new Foldout
{
text = $"{eventType.Name} (挂载数: {handlers.Length})",
style =
{
backgroundColor = new StyleColor(new Color(0.2f, 0.2f, 0.2f, 0.5f)),
marginTop = 2,
paddingBottom = 2,
borderBottomWidth = 1,
borderBottomColor = new StyleColor(new Color(0.1f, 0.1f, 0.1f))
}
};
foreach (var h in handlers) foldout.Add(CreateHandlerRow(h));
_subscribersScrollView.Add(foldout);
}
}
private void RecordEventLog(EventBase evt, string eventName, string senderInfo, EventHandlerInfo[] handlers)
{
if (!EventBus.EnableDebugRecord) return;
_logRecords.Add(CreateEventLogRecord(evt, eventName, senderInfo, handlers));
if (_logRecords.Count > MaxLogCount)
_logRecords.RemoveAt(0);
RequestLiveTrackerRefresh();
}
private EventLogRecord CreateEventLogRecord(EventBase evt, string eventName, string senderInfo,
EventHandlerInfo[] handlers)
{
var timestamp = DateTime.Now;
var hasListeners = handlers.Length > 0;
var paramsList = DumpEventParams(evt);
var handlerRows = new List<HandlerDisplayData>(handlers.Length);
foreach (var handler in handlers)
handlerRows.Add(ExtractHandlerDisplayData(handler));
var isCancelable = (GetMemberValue(evt, "IsCancelable") as bool?) ?? false;
var isCanceled = (GetMemberValue(evt, "IsCanceled") as bool?) ?? false;
var resultStr = GetMemberValue(evt, "Result")?.ToString() ?? "DEFAULT";
var phaseStr = GetMemberValue(evt, "Phase")?.ToString() ?? "NORMAL";
return new EventLogRecord
{
Timestamp = timestamp,
TimestampText = timestamp.ToString("HH:mm:ss.fff"),
EventName = eventName,
SenderInfo = senderInfo,
HasListeners = hasListeners,
IsCancelable = isCancelable,
IsCanceled = isCanceled,
Result = resultStr,
Phase = phaseStr,
Parameters = paramsList,
Handlers = handlerRows,
MatchText = BuildLogMatchText(eventName, senderInfo, paramsList)
};
}
private string BuildLogMatchText(string eventName, string senderInfo, List<string> paramsList)
{
if (paramsList.Count == 0)
return $"{eventName}\n{senderInfo}";
return $"{eventName}\n{senderInfo}\n{string.Join("\n", paramsList)}";
}
private void RefreshLiveTrackerView()
{
if (_liveTrackerScrollView == null) return;
_isLiveTrackerDirty = false;
_nextLiveTrackerRefreshTime = EditorApplication.timeSinceStartup + LiveTrackerRefreshIntervalSeconds;
_liveTrackerScrollView.Clear();
var filteredRecords = new List<EventLogRecord>(_logRecords.Count);
for (var i = _logRecords.Count - 1; i >= 0; i--)
{
var record = _logRecords[i];
if (MatchesLiveLogFilter(record))
filteredRecords.Add(record);
}
if (_liveLogStatsLabel != null)
{
_liveLogStatsLabel.text = _collapseSameEventType
? $"显示 {filteredRecords.Count} / {_logRecords.Count} 条,按事件类型折叠"
: $"显示 {filteredRecords.Count} / {_logRecords.Count} 条";
}
if (filteredRecords.Count == 0)
{
_liveTrackerScrollView.Add(new HelpBox("当前没有符合条件的实时事件日志。", HelpBoxMessageType.Info));
return;
}
if (_collapseSameEventType)
{
foreach (var group in BuildCollapsedGroups(filteredRecords))
_liveTrackerScrollView.Add(CreateCollapsedGroupElement(group));
return;
}
foreach (var record in filteredRecords)
_liveTrackerScrollView.Add(CreateLogEntryElement(record));
}
private bool MatchesLiveLogFilter(EventLogRecord record)
{
switch (_liveLogFilterMode)
{
case LiveLogFilterMode.HasListeners:
if (!record.HasListeners) return false;
break;
case LiveLogFilterMode.NoListeners:
if (record.HasListeners) return false;
break;
}
if (string.IsNullOrWhiteSpace(_liveLogSearchString))
return true;
return record.MatchText.IndexOf(_liveLogSearchString.Trim(), StringComparison.OrdinalIgnoreCase) >= 0;
}
private List<EventLogGroup> BuildCollapsedGroups(List<EventLogRecord> records)
{
var groups = new List<EventLogGroup>();
var groupMap = new Dictionary<string, EventLogGroup>(StringComparer.Ordinal);
foreach (var record in records)
{
if (!groupMap.TryGetValue(record.EventName, out var group))
{
group = new EventLogGroup { EventName = record.EventName };
groupMap[record.EventName] = group;
groups.Add(group);
}
group.Records.Add(record);
}
return groups;
}
private VisualElement CreateCollapsedGroupElement(EventLogGroup group)
{
var latest = group.LatestRecord;
var foldout = new Foldout
{
text = $"{group.EventName} × {group.Records.Count} 最近: {latest.TimestampText}",
value = group.Records.Count == 1,
style =
{
backgroundColor = new StyleColor(new Color(0.2f, 0.2f, 0.2f, 0.4f)),
marginTop = 2,
paddingBottom = 2,
borderBottomWidth = 1,
borderBottomColor = new StyleColor(new Color(0.1f, 0.1f, 0.1f))
}
};
var summary = new Label(
$"最近来源: {latest.SenderInfo} | 监听者: {(latest.HasListeners ? latest.Handlers.Count : 0)} | 已取消: {(latest.IsCanceled ? "是" : "否")}")
{
style =
{
marginLeft = 18,
marginBottom = 4,
color = new StyleColor(Color.gray)
}
};
foldout.Add(summary);
if (foldout.value)
PopulateCollapsedGroupDetails(foldout, group, 1);
foldout.RegisterValueChangedCallback(evt =>
{
if (evt.newValue)
{
PopulateCollapsedGroupDetails(foldout, group, 1);
return;
}
ClearFoldoutChildren(foldout, 1);
});
return foldout;
}
private void PopulateCollapsedGroupDetails(Foldout foldout, EventLogGroup group, int keepChildCount)
{
if (foldout.childCount > keepChildCount) return;
foreach (var record in group.Records)
{
var row = CreateLogEntryElement(record);
row.style.marginLeft = 12;
foldout.Add(row);
}
}
private void ClearFoldoutChildren(Foldout foldout, int keepChildCount)
{
while (foldout.childCount > keepChildCount)
foldout.RemoveAt(foldout.childCount - 1);
}
private VisualElement CreateLogEntryElement(EventLogRecord record)
{
var logEntry = new VisualElement
{
style =
{
flexDirection = FlexDirection.Column,
paddingBottom = 4,
paddingTop = 4,
borderBottomWidth = 1,
borderBottomColor = new StyleColor(new Color(0.15f, 0.15f, 0.15f))
}
};
var headerRow = new VisualElement
{ style = { flexDirection = FlexDirection.Row, alignItems = Align.Center } };
headerRow.Add(new Label(record.TimestampText)
{ style = { width = 80, color = new StyleColor(Color.gray) } });
headerRow.Add(new Label(record.EventName)
{
style = { color = new StyleColor(new Color(1f, 0.84f, 0f)), unityFontStyleAndWeight = FontStyle.Bold }
});
var senderClassName = GetSenderClassName(record.SenderInfo);
var senderLabel = new Label($" [由 {record.SenderInfo} 触发]")
{
style =
{
color = new StyleColor(new Color(0.5f, 0.8f, 1f)),
marginLeft = 5,
unityFontStyleAndWeight = FontStyle.Italic
}
};
if (senderClassName != "未知来源" && senderClassName != "EventBus" && senderClassName != "Unknown")
{
senderLabel.RegisterCallback<MouseEnterEvent>(_ =>
senderLabel.style.color = new StyleColor(Color.cyan));
senderLabel.RegisterCallback<MouseLeaveEvent>(_ =>
senderLabel.style.color = new StyleColor(new Color(0.5f, 0.8f, 1f)));
senderLabel.RegisterCallback<ClickEvent>(_ => OpenScriptByClassName(senderClassName));
}
headerRow.Add(senderLabel);
logEntry.Add(headerRow);
var statusRow = new VisualElement
{ style = { flexDirection = FlexDirection.Row, marginLeft = 80, marginTop = 2 } };
statusRow.Add(new Label($"可取消: {(record.IsCancelable ? "是" : "否")}")
{ style = { color = new StyleColor(Color.gray), marginRight = 15 } });
var canceledLabel = new Label($"已取消: {(record.IsCanceled ? "是" : "否")}")
{ style = { marginRight = 15 } };
canceledLabel.style.color =
record.IsCanceled ? new StyleColor(new Color(1f, 0.3f, 0.3f)) : new StyleColor(Color.gray);
statusRow.Add(canceledLabel);
statusRow.Add(new Label($"Result: {record.Result}")
{ style = { color = new StyleColor(new Color(0.8f, 0.6f, 0.8f)), marginRight = 15 } });
statusRow.Add(new Label($"最后阶段: {record.Phase}")
{ style = { color = new StyleColor(new Color(0.6f, 0.8f, 1f)) } });
logEntry.Add(statusRow);
var paramsContainer = new VisualElement
{ style = { flexDirection = FlexDirection.Column, marginLeft = 80, marginTop = 2, marginBottom = 2 } };
if (record.Parameters.Count == 0)
{
paramsContainer.Add(new Label("参数: { 无 }")
{ style = { color = new StyleColor(new Color(0.5f, 0.5f, 0.5f)) } });
}
else
{
paramsContainer.Add(
new Label("携带参数:") { style = { color = new StyleColor(new Color(0.6f, 0.8f, 1f)) } });
foreach (var pStr in record.Parameters)
paramsContainer.Add(new Label($" • {pStr}")
{ style = { color = new StyleColor(new Color(0.6f, 0.9f, 0.6f)), marginLeft = 10 } });
}
logEntry.Add(paramsContainer);
if (record.HasListeners)
{
foreach (var handler in record.Handlers)
logEntry.Add(CreateHandlerRow(handler, 80));
}
else
{
var noHandlerRow = new VisualElement
{ style = { flexDirection = FlexDirection.Row, marginLeft = 80, marginTop = 2 } };
noHandlerRow.Add(new Label("(无监听者响应)") { style = { color = new StyleColor(Color.gray) } });
logEntry.Add(noHandlerRow);
}
return logEntry;
}
private string GetSenderClassName(string senderInfo)
{
if (string.IsNullOrWhiteSpace(senderInfo))
return "未知来源";
return senderInfo.Contains(".") ? senderInfo.Split('.')[0] : senderInfo;
}
private VisualElement CreateHandlerRow(object h)
{
return CreateHandlerRow(ExtractHandlerDisplayData(h), 40);
}
private VisualElement CreateHandlerRow(HandlerDisplayData handlerData, float marginLeft)
{
var targetInstance = handlerData.TargetInstance;
var targetName = handlerData.TargetName;
var methodName = handlerData.MethodName;
var priority = handlerData.Priority;
var execType = handlerData.ExecType;
var row = new VisualElement
{
style =
{
flexDirection = FlexDirection.Row, marginTop = 2, marginBottom = 2, marginLeft = marginLeft,
alignItems = Align.Center
}
};
row.Add(new Label($"[{priority}]") { style = { width = 75, color = new StyleColor(Color.gray) } });
row.Add(new Label($"[{execType}]")
{ style = { width = 50, color = new StyleColor(new Color(0.8f, 0.6f, 0.2f)) } });
var linkLabel = new Label($"{targetName}.{methodName}()")
{ style = { color = new StyleColor(new Color(0.3f, 0.6f, 1f)) } };
linkLabel.RegisterCallback<MouseEnterEvent>(e => linkLabel.style.color = new StyleColor(Color.cyan));
linkLabel.RegisterCallback<MouseLeaveEvent>(e =>
linkLabel.style.color = new StyleColor(new Color(0.3f, 0.6f, 1f)));
linkLabel.RegisterCallback<ClickEvent>(e => OpenScriptByClassName(targetName));
linkLabel.style.flexGrow = 1;
row.Add(linkLabel);
if (handlerData.PingTarget)
{
var pingBtn = new Button(() =>
{
EditorGUIUtility.PingObject(handlerData.PingTarget.gameObject);
Selection.activeGameObject = handlerData.PingTarget.gameObject;
}) { text = "Ping", style = { width = 50, height = 18 } };
row.Add(pingBtn);
}
else if (handlerData.IsStaticHandler && methodName != "未知方法")
row.Add(new Label("(静态)")
{
style = { width = 50, color = new StyleColor(Color.gray), unityTextAlign = TextAnchor.MiddleRight }
});
else
row.Add(new Label("(无对象)")
{
style = { width = 50, color = new StyleColor(Color.gray), unityTextAlign = TextAnchor.MiddleRight }
});
return row;
}
private HandlerDisplayData ExtractHandlerDisplayData(object handlerObj)
{
var priority = GetMemberValue(handlerObj, "Priority") ?? "NORMAL";
var debugInfo = GetMemberValue(handlerObj, "DebugInfo") as string ?? "未知方法";
var execType = "Sync";
if (debugInfo.Contains("(UniTask)") || debugInfo.Contains("Async")) execType = "Async";
else if (debugInfo.Contains("(Task->UniTask)") || debugInfo.Contains("(Task)")) execType = "Task";
object targetInstance = null;
var targetName = "未知类";
var methodName = debugInfo;
var handlerDelegate = GetMemberValue(handlerObj, "Handler") as Delegate;
if (handlerDelegate != null)
{
targetInstance = handlerDelegate.Target;
var methodInfo = handlerDelegate.Method;
if (targetInstance != null && targetInstance.GetType().Name.Contains("Wrapper"))
{
var realDelegate = GetMemberValue(targetInstance, "_originalAction") as Delegate;
if (realDelegate != null)
{
targetInstance = realDelegate.Target;
methodInfo = realDelegate.Method;
}
}
if (methodInfo != null)
{
var rawClassName = methodInfo.DeclaringType?.Name ?? "未知类";
if (rawClassName.Contains("<>c")) rawClassName = "匿名闭包类";
targetName = rawClassName;
methodName = methodInfo.Name;
}
}
return new HandlerDisplayData
{
TargetInstance = targetInstance,
PingTarget = targetInstance as MonoBehaviour,
TargetName = targetName,
MethodName = methodName,
Priority = priority,
ExecType = execType,
IsStaticHandler = targetInstance == null
};
}
private List<string> DumpEventParams(EventBase evt)
{
var values = new List<string>();
if (evt == null) return values;
var currentType = evt.GetType();
while (currentType != null && currentType != typeof(EventBase) && currentType != typeof(object))
{
var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance |
BindingFlags.DeclaredOnly;
foreach (var f in currentType.GetFields(flags))
{
if (f.Name.Contains("<") || f.Name.Contains(">")) continue;
if (f.Name is "CurrentHandler" or "Phase") continue;
values.Add($"{f.Name} = {FormatValue(f.GetValue(evt))}");
}
foreach (var p in currentType.GetProperties(flags))
{
if (p.Name is "CurrentHandler" or "Phase" or "IsCanceled" or "IsCancelable" or "Result") continue;
if (!p.CanRead || p.GetIndexParameters().Length > 0) continue;
try
{
values.Add($"{p.Name} = {FormatValue(p.GetValue(evt))}");
}
catch
{
// ignored
}
}
currentType = currentType.BaseType;
}
return values;
}
private string FormatValue(object val)
{
if (val == null) return "null";
if (val is UnityEngine.Object uObj) return uObj ? uObj.name : "null(已销毁)";
if (val is string s) return $"\"{s}\"";
if (val is Array arr) return $"[数组 Length:{arr.Length}]";
return val.ToString();
}
private object GetMemberValue(object obj, string memberName)
{
if (obj == null) return null;
var type = obj.GetType();
var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
var prop = type.GetProperty(memberName, flags);
if (prop != null) return prop.GetValue(obj);
var field = type.GetField(memberName, flags);
if (field != null) return field.GetValue(obj);
return null;
}
private void ClearLogs()
{
_logRecords.Clear();
RequestLiveTrackerRefresh(true);
}
private void OpenScriptByClassName(string className)
{
if (className == "匿名闭包类") return;
var guids = AssetDatabase.FindAssets($"{className} t:MonoScript");
foreach (var guid in guids)
{
var path = AssetDatabase.GUIDToAssetPath(guid);
if (path.EndsWith($"{className}.cs"))
{
var script = AssetDatabase.LoadAssetAtPath<MonoScript>(path);
if (script)
{
AssetDatabase.OpenAsset(script);
return;
}
}
}
}
}
}
#endif