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

基于字符串键的变量设计:寻求更高效的算法或实现方案

优化字符串变量替换的性能方案(替代反射实现)

我正在设计一个支持设计师在编辑器中用{variableName}格式插入变量的文本区域,当前通过反射遍历类的FieldInfo获取值实现替换,但反射性能较差。需要找到功能、灵活性相当的更优实现方式。

原实现代码(可运行但效率低):

public static class FormattedString
{
    
    public static string GetFormattedString(string baseString, object obj)
    {
        if (!baseString.Contains('{'))
            return baseString;

        string formattedString = "";

        foreach (var split in baseString.Split('{'))
        {
            if (!split.Contains('}'))
            {
                formattedString += split;
            }
            else
            {
                int index = split.IndexOf('}');
                formattedString += GetFieldValue(split.Substring(0, index), obj);

                if (index < split.Length)
                    formattedString += split.Substring(index + 1);
            }
        }
        return formattedString;
    }

    public static string GetFieldValue(string name, object obj)
    {
        try
        {
            return obj.GetType().GetField(name).GetValue(obj).ToString();
        }
        catch
        {
            return "{Unable to find: "+ name+"}" ;
        }
    }
}

方案1:用表达式树缓存字段访问委托

反射的性能损耗主要在每次调用时的类型查找和字段获取,用表达式树编译一次字段访问逻辑并缓存,后续直接调用委托,能大幅提升性能。

实现代码:

public static class FormattedString
{
    // 缓存字段访问委托,键为类型+字段名的组合
    private static readonly Dictionary<string, Func<object, string>> _fieldAccessors = new Dictionary<string, Func<object, string>>();

    public static string GetFormattedString(string baseString, object obj)
    {
        if (string.IsNullOrEmpty(baseString) || !baseString.Contains('{'))
            return baseString;

        // 改用StringBuilder优化字符串拼接,原+=会产生大量临时字符串
        StringBuilder formattedString = new StringBuilder();

        foreach (var split in baseString.Split('{'))
        {
            if (!split.Contains('}'))
            {
                formattedString.Append(split);
            }
            else
            {
                int index = split.IndexOf('}');
                var fieldName = split.Substring(0, index);
                formattedString.Append(GetFieldValue(fieldName, obj));

                if (index < split.Length - 1)
                    formattedString.Append(split.Substring(index + 1));
            }
        }
        return formattedString.ToString();
    }

    public static string GetFieldValue(string name, object obj)
    {
        if (obj == null) return "{Unable to find: " + name + "}";

        var type = obj.GetType();
        var cacheKey = $"{type.FullName}.{name}";

        if (!_fieldAccessors.TryGetValue(cacheKey, out var accessor))
        {
            // 构建表达式树,编译成委托
            var param = Expression.Parameter(typeof(object), "obj");
            var cast = Expression.Convert(param, type);
            var field = Expression.Field(cast, name);
            var toString = Expression.Call(field, typeof(object).GetMethod("ToString"));
            var lambda = Expression.Lambda<Func<object, string>>(toString, param);
            accessor = lambda.Compile();

            _fieldAccessors[cacheKey] = accessor;
        }

        try
        {
            return accessor(obj);
        }
        catch
        {
            return "{Unable to find: " + name + "}";
        }
    }
}

优点:无需修改原有实体类,第一次调用编译后后续性能接近直接字段访问,兼顾灵活性和性能。

方案2:让实体类实现自定义变量访问接口

如果可以修改目标类的代码,定义一个统一的变量访问接口,直接通过方法获取值,完全避免反射开销。

实现代码:

// 定义变量访问接口
public interface IVariableProvider
{
    string GetVariable(string name);
}

// 目标实体类实现接口
public class UserInfo : IVariableProvider
{
    public string Username;
    public int Age;

    public string GetVariable(string name)
    {
        return name switch
        {
            "Username" => Username,
            "Age" => Age.ToString(),
            _ => "{Unable to find: " + name + "}"
        };
    }
}

// 修改格式化类
public static class FormattedString
{
    public static string GetFormattedString(string baseString, IVariableProvider provider)
    {
        if (string.IsNullOrEmpty(baseString) || !baseString.Contains('{'))
            return baseString;

        StringBuilder formattedString = new StringBuilder();

        foreach (var split in baseString.Split('{'))
        {
            if (!split.Contains('}'))
            {
                formattedString.Append(split);
            }
            else
            {
                int index = split.IndexOf('}');
                var varName = split.Substring(0, index);
                formattedString.Append(provider.GetVariable(varName));

                if (index < split.Length - 1)
                    formattedString.Append(split.Substring(index + 1));
            }
        }
        return formattedString.ToString();
    }
}

优点:性能最优,类型安全,可自定义变量的处理逻辑;缺点:需要修改所有目标实体类的代码,灵活性稍弱。

方案3:直接传递字典映射变量

如果变量集合是动态的,或者不想依赖实体类,直接用Dictionary<string, object>传递变量键值对,取值效率极高。

实现代码:

public static class FormattedString
{
    public static string GetFormattedString(string baseString, Dictionary<string, object> variables)
    {
        if (string.IsNullOrEmpty(baseString) || !baseString.Contains('{'))
            return baseString;

        StringBuilder formattedString = new StringBuilder();

        foreach (var split in baseString.Split('{'))
        {
            if (!split.Contains('}'))
            {
                formattedString.Append(split);
            }
            else
            {
                int index = split.IndexOf('}');
                var varName = split.Substring(0, index);
                if (variables.TryGetValue(varName, out var value))
                {
                    formattedString.Append(value?.ToString());
                }
                else
                {
                    formattedString.Append("{Unable to find: " + varName + "}");
                }

                if (index < split.Length - 1)
                    formattedString.Append(split.Substring(index + 1));
            }
        }
        return formattedString.ToString();
    }
}

优点:实现最简单,性能好,完全灵活;缺点:需要手动维护字典和变量的映射关系,适合临时或动态变量场景。

额外优化点

不管用哪种方案,都要把原代码中的字符串+=替换为StringBuilder,因为字符串是不可变类型,+=会频繁创建临时字符串,导致内存和性能损耗。

内容的提问来源于stack exchange,提问作者CardCastle Studio

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 15:01:27