You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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实例并存入缓存,再处理其依赖的双向引用,彻底打断无限递归的链条。具体实现如下:

  1. 新增字典作为缓存,记录Object1到已转换Object3的映射;
  2. 转换时先检查缓存,存在则直接返回,避免重复转换;
  3. 先通过原构造函数创建Object3(对引用类型暂时占位),立即存入缓存;
  4. 最后递归处理嵌套的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 10:40:17