通用引用管理框架

引言

在复杂的企业级应用中,我们经常需要处理对象图内部的引用关系更新问题。例如,在配置数据去重后,需要将所有指向旧 ID 的引用更新为新 ID;或者在序列化/反序列化过程中,将 JSON 字符串内的旧标识符批量替换。传统做法往往需要编写大量针对特定类型和属性的硬编码逻辑,不仅繁琐易错,而且难以复用。

本文介绍的通用引用管理框架正是为解决此类问题而设计的。它提供了一套高度可扩展的声明式机制,能够在运行时遍历任意复杂的对象图(包括嵌套对象、集合、字典、JSON 字符串),并根据预设规则自动完成引用值的更新。无论是简单的属性重命名,还是需要根据键值查找新对象的复杂映射,该框架都能优雅地应对。

核心概念

在深入代码细节之前,先理解框架的四个核心抽象:

引用目标(ReferenceTarget):一个枚举,表示引用的语义类型(主键、符号或自定义)。它为解析器提供了上下文信息,便于实现不同的解析策略。

引用解析器(IReferenceResolver):框架的执行核心,负责接收旧值,返回新值。开发者通过实现此接口或提供委托来定义具体的替换逻辑。

值访问器(IValueAccessor):对对象图中某一可读写位置的抽象封装。无论是对象属性、字段还是字典的键,都可以通过统一的 GetValue/SetValue 接口进行操作。

引用定义(ReferenceDefinition):描述"何处、何时、如何"应用解析规则。它指定了引用目标、是否递归、是否处理序列化内容,以及可选的筛选条件。

框架组件详解

  1. 值访问器体系
    框架内置了三种 IValueAccessor 实现,覆盖了绝大多数使用场景:

访问器类型 适用场景 读写目标

PropertyAccessor 公共可读写属性 PropertyInfo.SetValue

FieldAccessor 公共实例字段 FieldInfo.SetValue

DictionaryKeyAccessor 字典的键(需支持键替换) 先移除旧条目,再添加新键值对

这种抽象使得遍历器无需关心底层成员的具体类型,极大地简化了遍历逻辑。

  1. 序列化内容处理器
    当引用值被嵌入在 JSON 字符串中时,简单的值替换无法触及内部结构。框架为此设计了 ISerializedContentHandler 接口,并提供了三种常用实现:

JsonContentHandler:支持通过 JSONPath 定位并替换指定节点下的所有字符串值。

JsonArrayContentHandler:将 JSON 数组字符串解析为列表,逐项替换后重新序列化。

JsonObjectContentHandler:递归遍历 JSON 对象,替换所有叶子字符串值。

内部辅助类 JsonReferenceHelper 使用 JToken 实现了递归替换逻辑,保证了对任意复杂 JSON 结构的兼容性。

  1. 类型引用注册表(TypeReferenceRegistry)
    该注册表以类型为键,存储了一组 ReferenceDefinition 实例。它支持全局静态实例(TypeReferenceRegistry.Global),也允许按需创建独立实例。注册表内部使用 ConcurrentDictionary 保证线程安全,并通过快照缓存提高查询性能。

典型的注册操作如下:

csharp

// 为所有 string 类型的属性注册一个基于主键的引用定义

TypeReferenceRegistry.Global.Register(ReferenceTarget.PrimaryKey);

// 为自定义类型注册包含 JSON 处理的复杂定义

var jsonHandler = new JsonContentHandler("$.items[*].id");

TypeReferenceRegistry.Global.Register(

new ReferenceDefinition(ReferenceTarget.PrimaryKey,

isSerializedContent: true,

contentHandler: jsonHandler));

  1. 全集合遍历器(FullCollectionTraverser)

这是框架中最复杂的组件,负责深度优先遍历对象图。其核心特性包括:

循环引用检测:通过 HashSet 记录已访问对象,防止无限递归。

完整集合支持:正确处理 IEnumerable、IList、IDictionary。

字典键处理:支持对字典键的引用替换,并在键更改后保持值不变。

简单值集合内处理:对于 List 等简单值集合,可直接对其中的字符串应用解析规则。

遍历器在遇到每个成员时,会先查找该成员类型对应的引用定义。若存在匹配的定义,则调用解析器获取新值,并通过访问器写回对象。若定义中标记了 Recurse = true,则对新值继续进行递归遍历。

  1. 引用更新引擎(ReferenceUpdateEngine)
    引擎将解析器、遍历器和注册表组合为一个便捷的外观:

csharp

public class ReferenceUpdateEngine

{

public void Update(object obj);

public void UpdateAll(IEnumerable objects);

}

只需传入根对象,引擎便会自动完成整棵对象图的引用更新。

工作流程

下图展示了框架处理一个对象的完整流程:

高级特性:声明式标记与字段级映射

  1. 特性标记(ReferenceAttribute)
    对于不希望集中管理注册表的场景,框架提供了 [Reference] 特性,允许直接在模型类的属性或字段上声明引用行为:

随后通过扩展方法 ResolveAttributes 即可应用这些规则:

csharp

node.ResolveAttributes(resolver);

  1. 字段级引用映射机制(FieldMapping 命名空间)

当引用解析需要跨对象查找时,例如根据旧 ID 找到目标对象并提取其新属性值,FieldMappingReferenceResolver 提供了声明式配置能力。

csharp

var mappings = new List

{

new FieldReferenceMapping

{

SourcePropertyPath = "NextNodeKey", // 当前对象的属性路径

TargetPropertyPath = "Callkey", // 目标对象上的属性路径

Mode = FieldReferenceMode.ByValueAsNodeIdentifier

}

};

var nodeLookup = new NodeConfigLookup(id => nodeDictionary.GetValueOrDefault(id));

var resolver = new FieldMappingReferenceResolver(mappings, nodeLookup, renameMap);

该机制支持三种解析模式:

ByValueAsNodeIdentifier:旧值作为节点标识符,查找节点后取其指定属性。

ByValueDirectRename:直接通过重命名映射字典转换旧值。

Custom:提供自定义委托实现任意复杂逻辑。

使用示例

示例 1:全局 ID 重映射

假设我们在配置合并后获得了新旧 ID 的映射字典,现在需要更新整个配置对象图:

csharp

var resolver = new DelegateResolver((oldValue, target, ctx) =>

{

if (oldValue is string id && idMap.TryGetValue(id, out var newId))

return newId;

return oldValue;

});

TypeReferenceRegistry.Global.Register(ReferenceTarget.PrimaryKey);

var engine = new ReferenceUpdateEngine(resolver);

engine.Update(rootConfig);

示例 2:结合 JSON 内容处理

某配置节点的 RawData 属性是一个 JSON 字符串,其中包含待替换的 ID:

csharp

var handler = new JsonContentHandler("$.entities[*].refId");

var definition = new ReferenceDefinition(

ReferenceTarget.PrimaryKey,

isSerializedContent: true,

contentHandler: handler);

TypeReferenceRegistry.Global.Register(definition);

// 使用相同的解析器执行更新

engine.Update(rootNode);

示例 3:使用字段映射更新复杂引用

对于对话树节点,需要将 NextNodeKey 指向的节点的新 Callkey 写入当前属性:

csharp

var builder = new FieldMappingResolverBuilder()

.AddMapping("NextNodeKey", "Callkey", FieldReferenceMode.ByValueAsNodeIdentifier)

.WithNodeLookup(new NodeConfigLookup(id => allNodes.FirstOrDefault(n => n.Callkey == id)))

.WithRenameMaps(renameMap: callkeyRenameMap);

var resolver = builder.Build();

var engine = new ReferenceUpdateEngine(resolver);

engine.Update(dialogueGraph);

总结

通用引用管理框架通过清晰的抽象分层和灵活的扩展点,将原本散落在各处的引用更新逻辑统一为声明式配置。其核心价值在于:

通用性:适用于任何 .NET 对象图,无需修改模型类。

可扩展性:通过实现 IReferenceResolver 或 ISerializedContentHandler 即可支持新的解析策略。

高性能:反射信息缓存、访问器复用、线程安全的并发字典。

易用性:提供全局注册表、特性标记、流畅构建器等多种使用方式,适应不同偏好。

无论是配置数据迁移、序列化内容修复,还是复杂的图数据重连任务,该框架都能显著降低开发成本,提高代码的可维护性。建议将其纳入团队的基础设施库,在遇到引用更新需求时优先考虑使用。

csharp 复制代码
#region 通用引用管理框架(全集合支持版)

namespace ReferenceManagement
{
#nullable enable
    using Newtonsoft.Json;
    using Newtonsoft.Json.Linq;
    using System;
    using System.Collections;
    using System.Collections.Concurrent;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using System.Runtime.CompilerServices;

    // ==================== 核心类型 ====================

    public enum ReferenceTarget
    {
        PrimaryKey,
        Symbol,
        Custom
    }

    public class ReferenceContext
    {
        public object? CurrentObject { get; set; }
        public string? Path { get; set; }
        public Dictionary<string, object?> CustomData { get; set; } = new();
        public object? UserContext { get; set; }
        public Type? TargetType { get; set; }

        public ReferenceContext Clone()
        {
            return new ReferenceContext
            {
                CurrentObject = this.CurrentObject,
                Path = this.Path,
                UserContext = this.UserContext,
                CustomData = new Dictionary<string, object?>(this.CustomData),
                TargetType = this.TargetType
            };
        }
    }

    public interface IReferenceResolver
    {
        object? Resolve(object? oldValue, ReferenceTarget target, ReferenceContext context);
    }

    /// <summary>
    /// 将委托包装为 IReferenceResolver 的内部实现。
    /// 用于扩展方法中简化调用。
    /// </summary>
    internal class DelegateResolver : IReferenceResolver
    {
        private readonly Func<object?, ReferenceTarget, ReferenceContext, object?> _resolveFunc;
        public DelegateResolver(Func<object?, ReferenceTarget, ReferenceContext, object?> resolveFunc)
            => _resolveFunc = resolveFunc ?? throw new ArgumentNullException(nameof(resolveFunc));
        public object? Resolve(object? oldValue, ReferenceTarget target, ReferenceContext context)
            => _resolveFunc(oldValue, target, context);
    }

    // ==================== 值访问器(统一读写) ====================

    /// <summary>
    /// 表示对象图中一个可读写的值位置
    /// </summary>
    public interface IValueAccessor
    {
        object? GetValue();
        void SetValue(object? newValue);
        Type ValueType { get; }
        string Description { get; } // 用于调试/日志
    }

    /// <summary>
    /// 属性访问器
    /// </summary>
    public class PropertyAccessor : IValueAccessor
    {
        private readonly object _target;
        private readonly PropertyInfo _property;
        public PropertyAccessor(object target, PropertyInfo property)
        {
            _target = target;
            _property = property;
        }
        public object? GetValue() => _property.GetValue(_target);
        public void SetValue(object? newValue) => _property.SetValue(_target, newValue);
        public Type ValueType => _property.PropertyType;
        public string Description => $"{_target.GetType().Name}.{_property.Name}";
    }

    /// <summary>
    /// 字段访问器
    /// </summary>
    public class FieldAccessor : IValueAccessor
    {
        private readonly object _target;
        private readonly FieldInfo _field;
        public FieldAccessor(object target, FieldInfo field)
        {
            _target = target;
            _field = field;
        }
        public object? GetValue() => _field.GetValue(_target);
        public void SetValue(object? newValue) => _field.SetValue(_target, newValue);
        public Type ValueType => _field.FieldType;
        public string Description => $"{_target.GetType().Name}.{_field.Name}";
    }

    /// <summary>
    /// <summary>
    /// 字典键访问器,用于在字典遍历过程中支持键的替换。
    /// </summary>
    /// <remarks>
    /// 该类实现了 IValueAccessor 接口,允许在遍历字典时修改字典的键。
    /// 当键被修改时,会自动更新字典中的键值对,保持对应的值不变。
    /// </remarks>
    internal class DictionaryKeyAccessor : IValueAccessor
    {
        private readonly IDictionary _dict;
        private object? _currentKey;
        private readonly object _originalKey;

        public DictionaryKeyAccessor(IDictionary dict, object key)
        {
            _dict = dict;
            _originalKey = key;
            _currentKey = key;
        }

        public object? GetValue() => _currentKey;
        public void SetValue(object? newValue)
        {
            if (Equals(newValue, _currentKey)) return;
            if (newValue == null) return;
            var value = _dict[_currentKey];
            _dict.Remove(_currentKey);
            _currentKey = newValue;
            _dict[_currentKey] = value;
        }
        public Type ValueType => _currentKey?.GetType() ?? typeof(object);
        public string Description => $"DictionaryKey[{_originalKey}]";
    }

    // ==================== 序列化内容处理器 ====================

    public interface ISerializedContentHandler
    {
        string Process(string content, ReferenceTarget target, ReferenceContext context, IReferenceResolver resolver);
    }

    /// <summary>
    /// JSON 引用替换辅助类,提供通用的 JSON Token 引用替换逻辑。
    /// </summary>
    internal static class JsonReferenceHelper
    {
        public static JToken ReplaceReferences(JToken token, ReferenceTarget target, ReferenceContext context, IReferenceResolver resolver)
        {
            return token.Type switch
            {
                JTokenType.String => new JValue(
                    resolver.Resolve(token.Value<string>(), target, context)?.ToString()!),
                JTokenType.Object => new JObject(
                    ((JObject)token).Properties().Select(p =>
                        new JProperty(p.Name, ReplaceReferences(p.Value, target, context, resolver)))),
                JTokenType.Array => new JArray(
                    ((JArray)token).Select(t => ReplaceReferences(t, target, context, resolver))),
                _ => token
            };
        }
    }

    public class JsonContentHandler : ISerializedContentHandler
    {
        private readonly string? _jsonPath;
        public JsonContentHandler(string? jsonPath = null) => _jsonPath = jsonPath;
        public JsonContentHandler() : this(null) { }
        public string Process(string content, ReferenceTarget target, ReferenceContext context, IReferenceResolver resolver)
        {
            var token = JToken.Parse(content);
            var targetToken = string.IsNullOrEmpty(_jsonPath) ? token : token.SelectToken(_jsonPath!);
            if (targetToken == null)
                throw new InvalidOperationException($"JSON path '{_jsonPath}' not found in content.");
            var newToken = JsonReferenceHelper.ReplaceReferences(targetToken, target, context, resolver);
            if (targetToken != token)
            {
                targetToken.Replace(newToken);
                return token.ToString(Formatting.None);
            }
            return newToken.ToString(Formatting.None);
        }
    }

    // ==================== 引用定义(支持任意类型和集合) ====================

    /// <summary>
    /// 引用定义,描述一个具体的值位置应该如何被解析
    /// </summary>
    public class ReferenceDefinition
    {
        public ReferenceTarget Target { get; }
        public bool IsSerializedContent { get; }
        public ISerializedContentHandler? ContentHandler { get; }
        public bool Recurse { get; }

        // 可选的筛选器:当访问器满足条件时应用此定义
        public Func<IValueAccessor, bool>? Condition { get; set; }

        public ReferenceDefinition(ReferenceTarget target, bool isSerializedContent = false,
            ISerializedContentHandler? contentHandler = null, bool recurse = true)
        {
            Target = target;
            IsSerializedContent = isSerializedContent;
            ContentHandler = contentHandler;
            Recurse = recurse;
        }
    }

    // ==================== 类型引用定义存储 ====================

    public class TypeReferenceRegistry
    {
        private readonly ConcurrentDictionary<Type, List<ReferenceDefinition>> _typeDefinitions = new();
        private readonly ConcurrentDictionary<Type, List<ReferenceDefinition>> _valueTypeDefinitions = new(); // 值类型(string/int等)
        private readonly ConcurrentDictionary<Type, IReadOnlyList<ReferenceDefinition>> _cache = new();

        public static TypeReferenceRegistry Global { get; } = new();

        /// <summary>
        /// 为特定类型注册引用定义(应用于该类型的所有实例成员)
        /// </summary>
        public void Register<T>(ReferenceDefinition definition) => Register(typeof(T), definition);

        public void Register(Type type, ReferenceDefinition definition)
        {
            if (type == null) throw new ArgumentNullException(nameof(type));
            if (definition == null) throw new ArgumentNullException(nameof(definition));

            var dict = type.IsValueType || type == typeof(string) ? _valueTypeDefinitions : _typeDefinitions;
            var list = dict.GetOrAdd(type, _ => new List<ReferenceDefinition>());
            lock (list)
            {
                list.Add(definition);
                _cache.TryRemove(type, out _);
            }
        }

        /// <summary>
        /// 为特定类型注册一个简单的引用目标(快捷方式)
        /// </summary>
        public void Register<T>(ReferenceTarget target, bool isSerializedContent = false,
            ISerializedContentHandler? handler = null, bool recurse = true)
            => Register<T>(new ReferenceDefinition(target, isSerializedContent, handler, recurse));

        /// <summary>
        /// 为值类型或字符串注册引用定义(例如 List&lt;string&gt; 中的每个字符串)
        /// </summary>
        public void RegisterValue<T>(ReferenceTarget target) where T : struct
            => Register<T>(new ReferenceDefinition(target));

        public void RegisterString(ReferenceTarget target) => Register<string>(new ReferenceDefinition(target));

        public bool TryGetDefinitions(Type type, out IReadOnlyList<ReferenceDefinition> definitions)
        {
            if (_cache.TryGetValue(type, out var cached))
            {
                definitions = cached;
                return true;
            }

            // 先查值类型定义,再查引用类型定义
            List<ReferenceDefinition>? list = null;
            if ((type.IsValueType || type == typeof(string)) && _valueTypeDefinitions.TryGetValue(type, out list))
            {
                definitions = CreateSnapshotAndCache(type, list);
                return true;
            }
            else if (_typeDefinitions.TryGetValue(type, out list))
            {
                definitions = CreateSnapshotAndCache(type, list);
                return true;
            }

            definitions = Array.Empty<ReferenceDefinition>();
            return false;
        }

        private IReadOnlyList<ReferenceDefinition> CreateSnapshotAndCache(Type type, List<ReferenceDefinition> list)
        {
            lock (list)
            {
                if (_cache.TryGetValue(type, out var cached))
                    return cached;

                var snapshot = list.ToArray();
                _cache[type] = snapshot;
                return snapshot;
            }
        }
    }

    // ==================== 对象遍历器(全集合支持) ====================

    public interface IObjectTraverser
    {
        void Traverse(object obj, IReferenceResolver resolver, TypeReferenceRegistry registry, ReferenceContext context);
    }

    /// <summary>
    /// 缓存类型的可写属性和字段信息,避免重复反射
    /// </summary>
    internal static class MemberCache
    {
        private static readonly ConcurrentDictionary<Type, List<PropertyInfo>> _writableProperties = new();
        private static readonly ConcurrentDictionary<Type, List<FieldInfo>> _publicFields = new();

        public static IReadOnlyList<PropertyInfo> GetWritableProperties(Type type)
        {
            return _writableProperties.GetOrAdd(type, t => t.GetProperties(BindingFlags.Public | BindingFlags.Instance)
                .Where(p => p.CanRead && p.CanWrite && p.GetIndexParameters().Length == 0)
                .ToList());
        }

        public static IReadOnlyList<FieldInfo> GetPublicFields(Type type)
        {
            return _publicFields.GetOrAdd(type, t => t.GetFields(BindingFlags.Public | BindingFlags.Instance).ToList());
        }
    }

    public class FullCollectionTraverser : IObjectTraverser
    {
        private readonly HashSet<object> _visited = new(ReferenceEqualityComparer.Instance);
        private readonly object _lock = new();
        private readonly bool _processDictionaryKeys;
        private readonly bool _processSimpleValuesInCollections;
        private readonly Dictionary<Type, IReadOnlyList<ReferenceDefinition>> _typeDefinitionsCache = new();

        public FullCollectionTraverser(bool processDictionaryKeys = true, bool processSimpleValuesInCollections = true)
        {
            _processDictionaryKeys = processDictionaryKeys;
            _processSimpleValuesInCollections = processSimpleValuesInCollections;
        }

        /// <summary>
        /// 遍历对象图,解析所有引用。
        /// </summary>
        /// <param name="obj">要遍历的根对象</param>
        /// <param name="resolver">引用解析器</param>
        /// <param name="registry">类型引用定义注册表</param>
        /// <param name="context">引用上下文</param>
        /// <remarks>
        /// 使用 _visited 集合防止循环引用导致的无限递归。
        /// 当对象被访问时添加到集合,处理完成后移除,允许同一对象在不同路径中被多次处理。
        /// 使用 _lock 对象确保线程安全。
        /// 根对象的路径表示为 "root"。
        /// </remarks>
        public void Traverse(object obj, IReferenceResolver resolver, TypeReferenceRegistry registry, ReferenceContext context)
        {
            if (obj == null) return;

            lock (_lock)
            {
                if (!_visited.Add(obj)) return;
            }

            try
            {
                // 处理根对象自身的引用定义(注意:根对象没有父访问器,无法替换,仅用于递归解析)
                ProcessObjectValue(obj, resolver, registry, context, accessor: null, path: "root");

                // 递归遍历成员
                TraverseMembers(obj, resolver, registry, context);
            }
            finally
            {
                lock (_lock)
                {
                    _visited.Remove(obj);
                }
            }
        }

        private void ProcessObjectValue(object value, IReferenceResolver resolver, TypeReferenceRegistry registry,
            ReferenceContext context, IValueAccessor? accessor, string? path = null)
        {
            var type = value.GetType();
            if (!registry.TryGetDefinitions(type, out var defs)) return;

            var valueContext = context.Clone();
            valueContext.CurrentObject = value;
            valueContext.Path = path;
            valueContext.TargetType = accessor?.ValueType;

            object? currentValue = value;
            foreach (var def in defs)
            {
                if (ShouldSkipDefinition(def, accessor)) continue;

                var newValue = ResolveValue(currentValue, def, resolver, valueContext, path);
                currentValue = UpdateValue(currentValue, newValue, accessor);

                if (def.Recurse && currentValue != null && !ReferenceEquals(currentValue, value))
                {
                    Traverse(currentValue, resolver, registry, valueContext);
                }
            }
        }

        private bool ShouldSkipDefinition(ReferenceDefinition def, IValueAccessor? accessor)
        {
            return def.Condition != null && accessor != null && !def.Condition(accessor);
        }

        private object? ResolveValue(object? currentValue, ReferenceDefinition def, IReferenceResolver resolver, ReferenceContext context, string? path)
        {
            if (!def.IsSerializedContent)
                return resolver.Resolve(currentValue, def.Target, context);

            if (def.ContentHandler == null)
                throw new InvalidOperationException($"IsSerializedContent is true but ContentHandler is null for path: {path ?? "root"}");

            if (currentValue is string strContent)
            {
                var newContent = def.ContentHandler.Process(strContent, def.Target, context, resolver);
                return newContent != strContent ? newContent : currentValue;
            }

            return resolver.Resolve(currentValue, def.Target, context);
        }

        private object? UpdateValue(object? currentValue, object? newValue, IValueAccessor? accessor)
        {
            if (Equals(newValue, currentValue)) return currentValue;

            if (accessor != null)
                accessor.SetValue(newValue);

            return newValue;
        }

        private void TraverseMembers(object obj, IReferenceResolver resolver, TypeReferenceRegistry registry, ReferenceContext baseContext)
        {
            if (obj is string) return;

            if (obj is IDictionary dict)
            {
                TraverseDictionary(dict, resolver, registry, baseContext);
                return;
            }

            if (obj is IEnumerable enumerable)
            {
                TraverseEnumerable(enumerable, resolver, registry, baseContext);
                return;
            }

            TraversePropertiesAndFields(obj, resolver, registry, baseContext);
        }

        private void TraversePropertiesAndFields(object obj, IReferenceResolver resolver, TypeReferenceRegistry registry, ReferenceContext baseContext)
        {
            var type = obj.GetType();

            // 处理属性
            foreach (var prop in MemberCache.GetWritableProperties(type))
            {
                ProcessMember(obj, prop.Name, new PropertyAccessor(obj, prop), resolver, registry, baseContext);
            }

            // 处理字段
            foreach (var field in MemberCache.GetPublicFields(type))
            {
                ProcessMember(obj, field.Name, new FieldAccessor(obj, field), resolver, registry, baseContext);
            }
        }

        private void ProcessMember(object obj, string memberName, IValueAccessor accessor, IReferenceResolver resolver, TypeReferenceRegistry registry, ReferenceContext baseContext)
        {
            var value = accessor.GetValue();
            if (value == null) return;

            var memberContext = baseContext.Clone();
            memberContext.Path = memberName;
            ProcessObjectValue(value, resolver, registry, memberContext, accessor, memberName);

            var newValue = accessor.GetValue();
            if (!ReferenceEquals(value, newValue) && newValue != null)
            {
                Traverse(newValue, resolver, registry, memberContext);
            }
            else if (!IsPrimitiveOrString(value.GetType()))
            {
                Traverse(value, resolver, registry, memberContext);
            }
        }

        private void TraverseEnumerable(IEnumerable enumerable, IReferenceResolver resolver, TypeReferenceRegistry registry, ReferenceContext baseContext)
        {
            var list = enumerable as IList;
            bool isMutable = list != null && !list.IsReadOnly;

            int index = 0;

            foreach (var item in enumerable)
            {
                if (item == null) { index++; continue; }

                var itemType = item.GetType();
                bool isSimple = IsPrimitiveOrString(itemType);

                if (isSimple && _processSimpleValuesInCollections)
                {
                    if (!_typeDefinitionsCache.TryGetValue(itemType, out var defs))
                    {
                        registry.TryGetDefinitions(itemType, out defs);
                        _typeDefinitionsCache[itemType] = defs;
                    }

                    if (defs.Count > 0)
                    {
                        var elemContext = baseContext.Clone();
                        elemContext.CurrentObject = item;
                        elemContext.Path = $"[{index}]";
                        object? currentValue = item;
                        foreach (var def in defs)
                        {
                            var newValue = resolver.Resolve(currentValue, def.Target, elemContext);
                            if (!Equals(newValue, currentValue))
                            {
                                currentValue = newValue;
                                if (isMutable) list![index] = currentValue;
                            }
                        }
                    }
                }
                else if (!isSimple)
                {
                    Traverse(item, resolver, registry, baseContext);
                }
                index++;
            }
        }

        private void TraverseDictionary(IDictionary dict, IReferenceResolver resolver, TypeReferenceRegistry registry, ReferenceContext baseContext)
        {
            var keys = new System.Collections.ArrayList(dict.Keys);

            foreach (var key in keys)
            {
                if (key == null) continue;

                var keyAccessor = new DictionaryKeyAccessor(dict, key);

                if (_processDictionaryKeys)
                {
                    var keyType = key.GetType();
                    if (!_typeDefinitionsCache.TryGetValue(keyType, out var keyDefs))
                    {
                        registry.TryGetDefinitions(keyType, out keyDefs);
                        _typeDefinitionsCache[keyType] = keyDefs;
                    }

                    if (keyDefs.Count > 0)
                    {
                        ProcessObjectValue(key, resolver, registry, baseContext, keyAccessor, $"Key[{key}]");
                    }
                }

                var currentKey = keyAccessor.GetValue();

                if (currentKey != null && !IsPrimitiveOrString(currentKey.GetType()))
                {
                    Traverse(currentKey, resolver, registry, baseContext);
                }

                if (currentKey != null)
                {
                    ProcessDictionaryValue(dict, currentKey, baseContext, resolver, registry);
                }
            }
        }

        private void ProcessDictionaryValue(IDictionary dict, object key, ReferenceContext baseContext, IReferenceResolver resolver, TypeReferenceRegistry registry)
        {
            if (key == null) return;
            var value = dict[key];
            if (value == null) return;

            var valueType = value.GetType();
            bool isSimple = IsPrimitiveOrString(valueType);

            if (isSimple && _processSimpleValuesInCollections)
            {
                if (!_typeDefinitionsCache.TryGetValue(valueType, out var valDefs))
                {
                    registry.TryGetDefinitions(valueType, out valDefs);
                    _typeDefinitionsCache[valueType] = valDefs;
                }

                if (valDefs.Count > 0)
                {
                    var valContext = baseContext.Clone();
                    valContext.CurrentObject = value;
                    valContext.Path = $"Value[{key}]";
                    object? currentValue = value;
                    foreach (var def in valDefs)
                    {
                        var newValue = resolver.Resolve(currentValue, def.Target, valContext);
                        if (!Equals(newValue, currentValue))
                        {
                            currentValue = newValue;
                            dict[key] = currentValue;
                        }
                    }
                }
            }
            else if (!isSimple)
            {
                Traverse(value, resolver, registry, baseContext);
            }
        }

        private static bool IsPrimitiveOrString(Type type) => type.IsPrimitive || type == typeof(string);
    }

    internal class ReferenceEqualityComparer : IEqualityComparer<object>
    {
        public static ReferenceEqualityComparer Instance { get; } = new();
        public bool Equals(object? x, object? y) => ReferenceEquals(x, y);
        public int GetHashCode(object obj) => RuntimeHelpers.GetHashCode(obj);
    }

    // ==================== 引用更新引擎 ====================

    public class ReferenceUpdateEngine
    {
        private readonly IReferenceResolver _resolver;
        private readonly IObjectTraverser _traverser;
        private readonly TypeReferenceRegistry _registry;
        private readonly ReferenceContext _baseContext;

        public ReferenceUpdateEngine(
            IReferenceResolver resolver,
            IObjectTraverser? traverser = null,
            TypeReferenceRegistry? registry = null,
            object? userContext = null)
        {
            _resolver = resolver ?? throw new ArgumentNullException(nameof(resolver));
            _traverser = traverser ?? new FullCollectionTraverser();
            _registry = registry ?? TypeReferenceRegistry.Global;
            _baseContext = new ReferenceContext { UserContext = userContext };
        }

        public void Update(object obj)
        {
            if (obj == null) return;
            _traverser.Traverse(obj, _resolver, _registry, _baseContext);
        }

        public void UpdateAll(IEnumerable<object> objects)
        {
            foreach (var obj in objects) Update(obj);
        }
    }

    // ==================== 扩展方法 ====================

    public static class ReferenceExtensions
    {
        /// <summary>
        /// 使用指定的解析器更新对象图中的引用。
        /// 注意:每次调用都会创建新的引擎实例,批量处理请使用 UpdateAll。
        /// </summary>
        public static void UpdateReferences(this object obj, IReferenceResolver resolver, object? userContext = null)
            => new ReferenceUpdateEngine(resolver, userContext: userContext).Update(obj);

        /// <summary>
        /// 使用指定的解析引擎更新对象图中的引用(复用引擎实例)。
        /// </summary>
        public static void UpdateReferences(this object obj, ReferenceUpdateEngine engine)
            => engine.Update(obj);

        /// <summary>
        /// 使用委托函数更新对象图中的引用。
        /// 注意:每次调用都会创建新的引擎实例,批量处理请使用 UpdateAll。
        /// </summary>
        public static void UpdateReferences(this object obj, Func<object?, ReferenceTarget, ReferenceContext, object?> resolveFunc, object? userContext = null)
            => obj.UpdateReferences(new DelegateResolver(resolveFunc), userContext);

        /// <summary>
        /// 使用简单的字符串转换函数更新对象图中的引用。
        /// 注意:每次调用都会创建新的引擎实例,批量处理请使用 UpdateAll。
        /// </summary>
        public static void UpdateReferencesSimple(this object obj, Func<string, string> resolve, object? userContext = null)
            => obj.UpdateReferences((old, _, ctx) => old is string s ? resolve(s) : old, userContext);
    }

    // ==================== 声明式引用标记系统 ====================

    /// <summary>
    /// 标记属性为引用字段,支持声明式配置引用解析行为。
    /// </summary>
    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
    public class ReferenceAttribute : Attribute
    {
        /// <summary> 引用目标类型 </summary>
        public ReferenceTarget Target { get; set; } = ReferenceTarget.PrimaryKey;

        /// <summary>
        /// 是否为序列化内容(如 JSON 字符串)。
        /// 若为 true,将使用 ContentHandlerType 处理内容。
        /// </summary>
        public bool IsSerializedContent { get; set; } = false;

        /// <summary>
        /// 内容处理器类型,需实现 ISerializedContentHandler 接口。
        /// 仅当 IsSerializedContent 为 true 时生效。
        /// </summary>
        public Type? ContentHandlerType { get; set; }

        /// <summary>
        /// 内容处理器的构造函数参数(可选)。
        /// 支持传递给 ContentHandlerType 构造函数的参数,用于需要参数的处理器。
        /// 例如:JsonContentHandler 需要 jsonPath 参数。
        /// </summary>
        public object?[]? ContentHandlerArgs { get; set; }

        /// <summary>
        /// 是否递归解析解析后的值。
        /// </summary>
        public bool Recurse { get; set; } = true;
    }

    /// <summary>
    /// JSON 数组内容处理器,解析 JSON 数组字符串中的引用。
    /// </summary>
    public class JsonArrayContentHandler : ISerializedContentHandler
    {
        public string Process(string content, ReferenceTarget target, ReferenceContext context, IReferenceResolver resolver)
        {
            if (string.IsNullOrEmpty(content)) return content;

            var items = JsonConvert.DeserializeObject<List<string>>(content);
            if (items == null || items.Count == 0) return content;

            for (int i = 0; i < items.Count; i++)
            {
                var resolved = resolver.Resolve(items[i], target, context);
                items[i] = resolved?.ToString() ?? items[i];
            }
            return JsonConvert.SerializeObject(items);
        }
    }

    /// <summary>
    /// JSON 对象内容处理器,递归解析 JSON 对象中的所有字符串值。
    /// </summary>
    public class JsonObjectContentHandler : ISerializedContentHandler
    {
        public string Process(string content, ReferenceTarget target, ReferenceContext context, IReferenceResolver resolver)
        {
            if (string.IsNullOrEmpty(content)) return content;

            var token = JToken.Parse(content);
            var newToken = JsonReferenceHelper.ReplaceReferences(token, target, context, resolver);
            return newToken.ToString(Formatting.None);
        }
    }

    // ==================== 声明式引用标记支持 ====================

    /// <summary>
    /// 使用 [Reference] 特性定义解析对象的引用。
    /// </summary>
    public static class ReferenceAttributeExtensions
    {
        /// <summary>
        /// 使用 [Reference] 特性定义解析对象的引用。
        /// </summary>
        public static void ResolveAttributes(this object obj, IReferenceResolver resolver, object? userContext = null)
        {
            var provider = new AttributeReferenceDefinitionProvider();
            var definitions = provider.GetDefinitions(obj.GetType());
            if (definitions == null || definitions.Count == 0) return;

            var type = obj.GetType();
            var registry = new TypeReferenceRegistry();
            foreach (var kvp in definitions)
            {
                var memberName = kvp.Key;
                var def = kvp.Value;
                registry.Register(type, new ReferenceDefinition(def.Target, def.IsSerializedContent, def.ContentHandler, def.Recurse)
                {
                    Condition = accessor => accessor.Description.EndsWith($".{memberName}")
                });
            }

            var engine = new ReferenceUpdateEngine(resolver, registry: registry, userContext: userContext);
            engine.Update(obj);
        }
    }

    /// <summary>
    /// 基于特性的引用定义提供器,从 [Reference] 特性提取引用定义。
    /// </summary>
    public class AttributeReferenceDefinitionProvider
    {
        private readonly ConcurrentDictionary<Type, Dictionary<string, ReferenceDefinition>> _cache = new();

        public IReadOnlyDictionary<string, ReferenceDefinition>? GetDefinitions(Type type)
        {
            if (_cache.TryGetValue(type, out var cached))
                return cached;

            var definitions = new Dictionary<string, ReferenceDefinition>();
            var properties = type.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
            var fields = type.GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);

            foreach (var prop in properties)
            {
                var attr = prop.GetCustomAttributes(typeof(ReferenceAttribute), true).FirstOrDefault() as ReferenceAttribute;
                if (attr != null)
                {
                    definitions[prop.Name] = CreateDefinition(attr);
                }
            }

            foreach (var field in fields)
            {
                var attr = field.GetCustomAttributes(typeof(ReferenceAttribute), true).FirstOrDefault() as ReferenceAttribute;
                if (attr != null)
                {
                    definitions[field.Name] = CreateDefinition(attr);
                }
            }

            if (definitions.Count == 0)
                return null;

            _cache[type] = definitions;
            return definitions;
        }

        private static ReferenceDefinition CreateDefinition(ReferenceAttribute attr)
        {
            ISerializedContentHandler? contentHandler = null;
            if (attr.IsSerializedContent && attr.ContentHandlerType != null)
            {
                var handler = attr.ContentHandlerArgs != null && attr.ContentHandlerArgs.Length > 0
                    ? Activator.CreateInstance(attr.ContentHandlerType, attr.ContentHandlerArgs)
                    : Activator.CreateInstance(attr.ContentHandlerType);
                contentHandler = handler as ISerializedContentHandler;

                if (handler != null && contentHandler == null)
                {
                    throw new InvalidOperationException($"ContentHandlerType {attr.ContentHandlerType.Name} does not implement ISerializedContentHandler interface.");
                }
            }

            return new ReferenceDefinition(attr.Target, attr.IsSerializedContent, contentHandler, attr.Recurse);
        }
    }
}

#region 声明式字段级引用映射机制

namespace ReferenceManagement.FieldMapping
{
    using System;
    using System.Collections.Concurrent;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;

    /// <summary>
    /// 字段引用映射配置
    /// </summary>
    public class FieldReferenceMapping
    {
        /// <summary>
        /// 源属性路径(例如 "NextNodeKey" 或 "TransitionMap.NextKey")
        /// 支持点分隔的多级路径
        /// </summary>
        public string SourcePropertyPath { get; set; } = string.Empty;

        /// <summary>
        /// 目标节点类型(用于精确匹配,可选)
        /// </summary>
        public Type? TargetNodeType { get; set; }

        /// <summary>
        /// 目标属性名(例如 "Callkey" 或 "Symbol")
        /// 支持点分隔的多级路径
        /// </summary>
        public string TargetPropertyPath { get; set; } = string.Empty;

        /// <summary>
        /// 解析模式:如何从旧值定位目标节点并获取新值
        /// </summary>
        public FieldReferenceMode Mode { get; set; } = FieldReferenceMode.ByValueAsNodeIdentifier;

        /// <summary>
        /// 自定义解析委托(优先级最高)
        /// 参数:旧值、源属性路径、当前节点对象、上下文
        /// 返回:新值
        /// </summary>
        public Func<object?, string, object, object?, object?>? CustomResolver { get; set; }
    }

    /// <summary>
    /// 字段引用解析模式
    /// </summary>
    public enum FieldReferenceMode
    {
        /// <summary>
        /// 旧值即为目标节点的标识符(如 Callkey 或 Symbol),
        /// 通过标识符查找节点,然后获取该节点的 TargetPropertyPath 属性值
        /// </summary>
        ByValueAsNodeIdentifier,

        /// <summary>
        /// 旧值本身就是目标节点上 TargetPropertyPath 的值(例如旧值就是目标 Callkey),
        /// 直接通过重命名映射(renameMap)将旧值转换为新值
        /// </summary>
        ByValueDirectRename,

        /// <summary>
        /// 使用自定义解析委托
        /// </summary>
        Custom
    }

    /// <summary>
    /// 字段级引用解析器,支持声明式映射配置
    /// </summary>
    public class FieldMappingReferenceResolver : IReferenceResolver
    {
        private const string PropertyPathSeparator = ".";
        private readonly IReadOnlyList<FieldReferenceMapping> _mappings;
        private readonly INodeLookup _nodeLookup;
        private readonly IReadOnlyDictionary<string, string>? _renameMap;
        private readonly IReadOnlyDictionary<string, string>? _symbolRenameMap;
        private readonly ConcurrentDictionary<string, FieldReferenceMapping?> _mappingCache = new();
        private readonly Dictionary<string, FieldReferenceMapping?> _exactMatchCache = new();

        public interface INodeLookup
        {
            object? FindNodeByIdentifier(string identifier);
            T? GetPropertyValue<T>(object node, string propertyPath);
        }

        public FieldMappingReferenceResolver(
            IEnumerable<FieldReferenceMapping> mappings,
            INodeLookup nodeLookup,
            IReadOnlyDictionary<string, string>? renameMap = null,
            IReadOnlyDictionary<string, string>? symbolRenameMap = null)
        {
            _mappings = mappings.ToList().AsReadOnly();
            _nodeLookup = nodeLookup ?? throw new ArgumentNullException(nameof(nodeLookup));
            _renameMap = renameMap ?? new Dictionary<string, string>();
            _symbolRenameMap = symbolRenameMap ?? new Dictionary<string, string>();

            PrebuildExactMatchCache();
        }

        private void PrebuildExactMatchCache()
        {
            foreach (var mapping in _mappings)
            {
                if (!string.IsNullOrEmpty(mapping.SourcePropertyPath))
                    _exactMatchCache[mapping.SourcePropertyPath] = mapping;
            }
        }

        public object? Resolve(object? oldValue, ReferenceTarget target, ReferenceContext context)
        {
            if (oldValue == null) return null;
            if (oldValue is not string strValue) return oldValue;

            // 根据当前路径查找匹配的映射
            var mapping = GetMappingForPath(context.Path);
            if (mapping == null)
                throw new InvalidOperationException($"No field reference mapping found for path '{context.Path}'.");

            // 优先使用自定义解析器
            if (mapping.Mode == FieldReferenceMode.Custom && mapping.CustomResolver != null)
            {
                return mapping.CustomResolver(oldValue, context.Path!, context.CurrentObject, context.UserContext);
            }

            // 根据模式解析
            return mapping.Mode switch
            {
                FieldReferenceMode.ByValueAsNodeIdentifier => ResolveByIdentifier(strValue, mapping, context),
                FieldReferenceMode.ByValueDirectRename => ResolveDirectRename(strValue, mapping),
                _ => throw new NotSupportedException($"Unsupported field reference mode: {mapping.Mode}")
            };
        }

        private FieldReferenceMapping? GetMappingForPath(string path)
        {
            if (string.IsNullOrEmpty(path)) return null;

            if (_exactMatchCache.TryGetValue(path, out var exactMatch))
                return exactMatch;

            return _mappingCache.GetOrAdd(path, p =>
                _mappings.FirstOrDefault(m => IsPathMatch(p, m.SourcePropertyPath))
            );
        }

        private static bool IsPathMatch(string currentPath, string mappingPath)
        {
            if (string.Equals(currentPath, mappingPath, StringComparison.Ordinal))
                return true;

            if (currentPath.Length > mappingPath.Length + 1 &&
                currentPath[currentPath.Length - mappingPath.Length - 1] == PropertyPathSeparator[0] &&
                currentPath.EndsWith(mappingPath, StringComparison.Ordinal))
                return true;

            return false;
        }

        private object? ResolveByIdentifier(string identifier, FieldReferenceMapping mapping, ReferenceContext context)
        {
            var actualIdentifier = TransformIdentifier(identifier);
            var targetNode = _nodeLookup.FindNodeByIdentifier(actualIdentifier);

            if (targetNode == null)
                throw new InvalidOperationException($"Target node not found for identifier '{actualIdentifier}'.");

            var newValue = _nodeLookup.GetPropertyValue<object>(targetNode, mapping.TargetPropertyPath);
            if (newValue == null)
                throw new InvalidOperationException($"Property '{mapping.TargetPropertyPath}' returned null for node '{actualIdentifier}'.");
            return newValue;
        }

        private string TransformIdentifier(string identifier)
        {
            string actualIdentifier = _renameMap!.GetValueOrDefault(identifier);
            actualIdentifier = _symbolRenameMap!.GetValueOrDefault(actualIdentifier);
            return actualIdentifier;
        }

        private object? ResolveDirectRename(string oldValue, FieldReferenceMapping mapping)
        {
            // 直接应用 renameMap 或 symbolRenameMap
            if (_renameMap!.TryGetValue(oldValue, out var newCallkey))
                return newCallkey;
            if (_symbolRenameMap!.TryGetValue(oldValue, out var newSymbol))
                return newSymbol;
            throw new InvalidOperationException($"No rename mapping found for value '{oldValue}'.");
        }
    }

    /// <summary>
    /// 默认节点查找实现(适用于 NodeConfig 类型)
    /// </summary>
    public class NodeConfigLookup : FieldMappingReferenceResolver.INodeLookup
    {
        private const string PropertyPathSeparator = ".";
        private readonly Func<string, object?> _findByIdentifier;
        private readonly ConcurrentDictionary<string, string[]> _pathCache = new();
        private readonly ConcurrentDictionary<(Type Type, string Property), PropertyInfo> _propertyCache = new();

        public NodeConfigLookup(Func<string, object?> findByIdentifier)
        {
            _findByIdentifier = findByIdentifier ?? throw new ArgumentNullException(nameof(findByIdentifier));
        }

        public object? FindNodeByIdentifier(string identifier)
        {
            return _findByIdentifier(identifier);
        }

        public T? GetPropertyValue<T>(object node, string propertyPath)
        {
            var value = GetPropertyValue(node, propertyPath);
            return value is T t ? t : default;
        }

        private object? GetPropertyValue(object node, string propertyPath)
        {
            if (string.IsNullOrEmpty(propertyPath))
                throw new ArgumentException("Property path cannot be null or empty.", nameof(propertyPath));

            var parts = _pathCache.GetOrAdd(propertyPath, p => p.Split(PropertyPathSeparator));
            object current = node;

            foreach (var part in parts)
            {
                if (current == null)
                    throw new InvalidOperationException($"Cannot navigate property path '{propertyPath}': intermediate object is null at '{part}'.");

                var cacheKey = (current.GetType(), part);
                var prop = _propertyCache.GetOrAdd(cacheKey, k =>
                    k.Type.GetProperty(k.Property, BindingFlags.Public | BindingFlags.Instance));

                if (prop == null)
                    throw new InvalidOperationException($"Property '{part}' not found on type '{current.GetType().FullName}' in path '{propertyPath}'.");
                current = prop.GetValue(current);
            }

            return current;
        }
    }

    /// <summary>
    /// 便捷的构建器,用于创建 FieldMappingReferenceResolver
    /// </summary>
    public class FieldMappingResolverBuilder
    {
        private readonly List<FieldReferenceMapping> _mappings = new();
        private FieldMappingReferenceResolver.INodeLookup? _nodeLookup;
        private IReadOnlyDictionary<string, string>? _renameMap;
        private IReadOnlyDictionary<string, string>? _symbolRenameMap;

        public FieldMappingResolverBuilder AddMapping(string sourcePropertyPath, string targetPropertyPath,
            FieldReferenceMode mode = FieldReferenceMode.ByValueAsNodeIdentifier)
        {
            _mappings.Add(new FieldReferenceMapping
            {
                SourcePropertyPath = sourcePropertyPath,
                TargetPropertyPath = targetPropertyPath,
                Mode = mode
            });
            return this;
        }

        public FieldMappingResolverBuilder AddCustomMapping(string sourcePropertyPath,
            Func<object?, string, object, object?, object?> customResolver)
        {
            _mappings.Add(new FieldReferenceMapping
            {
                SourcePropertyPath = sourcePropertyPath,
                Mode = FieldReferenceMode.Custom,
                CustomResolver = customResolver
            });
            return this;
        }

        public FieldMappingResolverBuilder WithNodeLookup(FieldMappingReferenceResolver.INodeLookup nodeLookup)
        {
            _nodeLookup = nodeLookup;
            return this;
        }

        public FieldMappingResolverBuilder WithRenameMaps(
            IReadOnlyDictionary<string, string>? renameMap,
            IReadOnlyDictionary<string, string>? symbolRenameMap = null)
        {
            _renameMap = renameMap;
            _symbolRenameMap = symbolRenameMap;
            return this;
        }

        public FieldMappingReferenceResolver Build()
        {
            if (_nodeLookup == null)
                throw new InvalidOperationException("NodeLookup is required.");
            return new FieldMappingReferenceResolver(_mappings, _nodeLookup, _renameMap, _symbolRenameMap);
        }
    }

    /// <summary>
    /// 扩展方法,方便在去重流程中集成字段级引用映射
    /// </summary>
    public static class FieldMappingExtensions
    {
        public static ReferenceUpdateEngine CreateEngineWithFieldMappings(
            IEnumerable<FieldReferenceMapping> mappings,
            FieldMappingReferenceResolver.INodeLookup nodeLookup,
            IReadOnlyDictionary<string, string>? renameMap = null,
            IReadOnlyDictionary<string, string>? symbolRenameMap = null,
            object? userContext = null)
        {
            var resolver = new FieldMappingReferenceResolver(mappings, nodeLookup, renameMap, symbolRenameMap);
            return new ReferenceUpdateEngine(resolver, userContext: userContext);
        }
    }
}

#endregion

#endregion
相关推荐
aq55356004 小时前
三大编程语言深度对比:C# vs 易语言 vs 汇编
开发语言·汇编·c#
独特的螺狮粉4 小时前
云隙一言:鸿蒙Flutter框架 实现的随机名言应用
开发语言·flutter·华为·架构·开源·harmonyos
光泽雨4 小时前
c# 文件编译的过程
开发语言·c#
zxy28472253014 小时前
使用正运动的仿真软件C#
c#·仿真·运动控制·正运动·无硬件
heimeiyingwang4 小时前
【架构实战】SQL调优实战:从执行计划到索引优化
数据库·sql·架构
两万五千个小时5 小时前
Claude Code 源码:Agent 工具 — 多 Agent 的路由与定义机制
人工智能·程序员·架构
三省持敬5 小时前
异步并发的“流量警察”:在C#中使用SemaphoreSlim进行并发控制的最佳实践
c#
lcj25115 小时前
【C语言】数据在内存中的存储
c语言·数据结构
旖-旎5 小时前
哈希表(字母异位次分组)(5)
数据结构·c++·算法·leetcode·哈希算法·散列表