C# 10.0双向引用对象循环转换的“鸡生蛋”问题求解
解决C#双向引用对象循环转换的无限递归问题
在C# 10.0开发中遇到双向引用对象的循环转换“鸡生蛋”问题:两个Object1实例互相包含,需将其转换为Object3类型,但转换其中一个时需先转换另一个,反之亦然,导致递归转换函数陷入无限循环。
曾尝试用引用传递方案,通过中间对象传递Object3变量引用,先半构造Object3存入缓存再完成转换,但该方案过于复杂。希望找到无需为Object3添加空构造函数的简洁解决办法。
问题示例代码
namespace chicken // Also I like formatting my code like this, so don't judge me { public class Object1 { // Object One and Two are of the same class public List<dynamic> contents = new List<dynamic>(); public Object1() {} public Object1(List<dynamic> contents) { this.contents = contents; } } public class Object3 { public string name; public Object3 friend; public string pet; public Object3(List<dynamic> converted) { this.name = converted[0]; this.friend = converted[1]; this.pet = converted[2]; } } public class Program { public static void Main(string[] args) { Object1 object1 = new Object1(); // Just to create the problem, they don't Object1 object2 = new Object1(); // get created like this in the actual code object1.contents = new List<dynamic> { "Steve Johnson", // This is example data, this order is supposed to be unknown object2, "Biscut", }; object2.contents = new List<dynamic> { "Bob Smith", object1, "Tiny", }; Object3 final = convert(object1); // Runs the conversion function } public static Object3 convert(Object1 obj) { List<dynamic> converted = new List<dynamic>(); // I need a sanitized list for Object3 for (int i = 0; i < obj.contents.Count; i++) { if (obj.contents[i] is Object1) { converted.Add(convert(obj.contents[i])); // Causes infinite loop due to chicken and egg problem continue; } converted.Add(obj.contents[i]); } Object3 object3 = new Object3(converted); // Again the list order is unknown return object3; } } }
解决方案
核心思路:使用对象缓存+分阶段转换
通过缓存已转换的对象避免重复转换,同时调整转换流程:先创建Object3实例并存入缓存,再处理其依赖的双向引用,彻底打断无限递归的链条。具体实现如下:
- 新增字典作为缓存,记录
Object1到已转换Object3的映射; - 转换时先检查缓存,存在则直接返回,避免重复转换;
- 先通过原构造函数创建
Object3(对引用类型暂时占位),立即存入缓存; - 最后递归处理嵌套的
Object1,并更新Object3的引用字段。
修改后的转换函数代码
public static Object3 convert(Object1 obj, Dictionary<Object1, Object3> cache = null) { // 初始化缓存,第一次调用时创建新字典 cache ??= new Dictionary<Object1, Object3>(); // 检查缓存,已转换过直接返回 if (cache.TryGetValue(obj, out var existingObj3)) return existingObj3; // 第一步:构建转换列表,非Object1元素直接加入,Object1元素暂时占位为null List<dynamic> convertedContents = new List<dynamic>(); foreach (var item in obj.contents) { convertedContents.Add(item is not Object1 ? item : null); } // 使用原构造函数创建Object3,立即存入缓存(关键:此时即使引用未填充,也能让后续递归直接获取) Object3 newObj3 = new Object3(convertedContents); cache.Add(obj, newObj3); // 第二步:处理嵌套的Object1,递归转换后更新对应的引用字段 foreach (var item in obj.contents) { if (item is Object1 nestedObj) { // 递归转换,此时如果nestedObj已在缓存中会直接返回,避免无限循环 Object3 nestedObj3 = convert(nestedObj, cache); // 根据实际逻辑更新Object3的引用字段,示例中对应friend字段 newObj3.friend = nestedObj3; } } return newObj3; }
方案说明
- 无需修改
Object3的构造函数,完全复用原有逻辑; - 缓存字典通过可选参数传递,外部调用无需手动创建;
- 分阶段转换先占位再填充引用,完美解决双向引用的循环依赖问题;
- 如果
contents中Object1的位置不固定(如示例注释所述顺序未知),可以通过类型判断找到所有Object1类型元素,对应更新Object3的引用字段即可。
内容的提问来源于stack exchange,提问作者GreenChild
相关产品推荐
相关产品推荐

