{"record":{"id":"4bf4a9d124eb241d","repo":"egametang/ET","slug":"runtimetype-fullname-4bf4a9","errorCode":null,"errorMessage":"检测到循环引用，类型: {runtimeType.FullName}","messagePattern":"检测到循环引用，类型: (.+?)","errorType":"exception","errorClass":"NotSupportedException","httpStatus":null,"severity":"error","filePath":"Packages/cn.etetet.config/Scripts/Model/Share/CSharpObjectCodeEmitter.cs","lineNumber":900,"sourceCode":"\n            return false;\n        }\n\n        private string Indent(int level)\n        {\n            return new string(' ', level * 4);\n        }\n\n        [EnableClass]\n        private sealed class EmitterContext\n        {\n            private readonly HashSet<object> visited = new(new ObjectReferenceComparer());\n\n            public void Enter(object value, Type runtimeType)\n            {\n                if (!this.visited.Add(value))\n                {\n                    throw new NotSupportedException($\"检测到循环引用，类型: {runtimeType.FullName}\");\n                }\n            }\n\n            public void Exit(object value)\n            {\n                this.visited.Remove(value);\n            }\n        }\n\n        [EnableClass]\n        private sealed class ObjectReferenceComparer : IEqualityComparer<object>\n        {\n            bool IEqualityComparer<object>.Equals(object x, object y)\n            {\n                return ReferenceEquals(x, y);\n            }\n\n            int IEqualityComparer<object>.GetHashCode(object obj)","sourceCodeStart":882,"sourceCodeEnd":918,"githubUrl":"https://github.com/egametang/ET/blob/5cab01f7a8bee5f49f4781eebe9e2b1c6d7ebe0f/Packages/cn.etetet.config/Scripts/Model/Share/CSharpObjectCodeEmitter.cs#L882-L918","documentation":"Thrown by EmitterContext.Enter when the same object instance is entered twice along the current emission path. The emitter tracks visited references to detect cycles in the object graph; a re-entry means the graph loops back on itself, which would otherwise recurse infinitely while generating C# literals.","triggerScenarios":"An object graph with a reference cycle: A references B and B references A; a parent/child collection where a child points back to its parent; a self-referential field (node.Next = node).","commonSituations":"Runtime data structures that legitimately cycle (linked lists, trees with parent pointers, bidirectional relationships) being passed to an exporter that only supports trees/DAGs.","solutions":["Break the cycle before emitting: set parent/back-references to null or replace them with id references.","Project the cyclic graph into an acyclic DTO (ids instead of object pointers) for export.","Mark back-reference fields as non-serialized so the emitter skips them."],"exampleFix":"// before\npublic class TreeNode\n{\n    public List<TreeNode> Children = new();\n    public TreeNode Parent; // cycle: child -> parent -> child\n}\n// after\npublic class TreeNode\n{\n    public List<TreeNode> Children = new();\n    [NonSerialized] public TreeNode Parent; // skip during export\n}","handlingStrategy":"validation","validationCode":"// detect cycles in the graph before emitting (BFS reference check)\nstatic bool HasCycle(object root)\n{\n    var visited = new HashSet<object>(new ObjectReferenceComparer());\n    var stack = new Stack<object>();\n    stack.Push(root);\n    while (stack.Count > 0)\n    {\n        var cur = stack.Pop();\n        if (cur == null) continue;\n        if (!visited.Add(cur)) return true;\n        foreach (var f in cur.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public))\n            if (f.GetValue(cur) is object child && !f.FieldType.IsValueType) stack.Push(child);\n    }\n    return false;\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Project cyclic runtime graphs into acyclic DTOs (id references) before export.","Mark back/parent references [NonSerialized].","Never pass linked-list/tree-with-parent structures directly to the emitter."],"tags":["csharp","unity","config","serialization","code-emitter","cycle"],"backgroundTag":null,"analyzedSha":"5cab01f7a8bee5f49f4781eebe9e2b1c6d7ebe0f","analyzedAt":"2026-08-13T21:10:40.377Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}