.net 如何使用字典进行字符串插值

fbcarpbf  于 2023-03-31  发布在  .NET
关注(0)|答案(3)|浏览(144)

我有一个key value pair的字典,我需要使用字符串插值,然后根据命名参数Map字符串。请帮助我。

// code removed for brevity
var rawmessage = "Hello my name is {Name}"; // raw message
var dict= new Dictionary<string, object>();
dict.Add("{Name}", response.Name);

我希望能够做到这样的事情:

// Pseudo code
$"Hello my name is {Key}", dict;

到目前为止我找到的解决方案:

var message = parameters.Aggregate(message, 
(current, parameter) => current.Replace(parameter.Key, parameter.Value.ToString()));

它工作正常,但我的经理真的想让我使用字符串插值与字典。我不知道如何实现这一点。请指导我。

mklgxw1f

mklgxw1f1#

问题是,C#中的string interpolation只是string.Format的语法糖 Package 器。字符串被编译为相同的代码。以下面的代码为例:

public string GetGreeting1(string name)
{
    return string.Format("Hello {0}!", name);
}

public string GetGreeting2(string name)
{
    return $"Hello {name}!";
}

两种方法的IL输出相同:

GetGreeting1:
IL_0000:  ldstr       "Hello {0}!"
IL_0005:  ldarg.1     
IL_0006:  call        System.String.Format
IL_000B:  ret         

GetGreeting2:
IL_0000:  ldstr       "Hello {0}!"
IL_0005:  ldarg.1     
IL_0006:  call        System.String.Format
IL_000B:  ret

所以你的解决方案是完全有效的,我以前也用过。唯一可能的改进是如果你打算做大量的替换,如果你切换到使用StringBuilder,你可能会发现性能上的好处。你可能会在Nuget上找到一个库来为你做这件事,但这可能只是为了你的目的而矫枉过正。
作为额外的,我为此做了一个扩展类。作为额外的奖励,我还添加了一个方法,用于使用具有任意数量参数的对象,该方法使用反射来进行替换:

public static class StringReplacementExtensions
{
    public static string Replace(this string source, Dictionary<string, string> values)
    {
        return values.Aggregate(
            source,
            (current, parameter) => current
                .Replace($"{{{parameter.Key}}}", parameter.Value));
    }

    public static string Replace(this string source, object values)
    {
        var properties = values.GetType().GetProperties();

        foreach (var property in properties)
        {
            source = source.Replace(
                $"{{{property.Name}}}", 
                property.GetValue(values).ToString());
        }

        return source;
    }
}

并像这样使用:

var source = "Hello {name}!";

//Using Dictionary:
var dict = new Dictionary<string, string> { { "name", "David" } };
var greeting1 = source.Replace(dict);
Console.WriteLine(greeting1);

//Using an anonymous object:
var greeting2 = source.Replace(new { name = "David" });
Console.WriteLine(greeting2);
brqmpdu1

brqmpdu12#

而且它工作得很好,但是我的经理真的希望我用字典来使用字符串插值,我不确定如何实现这一点。
你不能这样做。C#插值字符串不是一个模板引擎,而是一个编译时特性,它转换为String.Format调用。这就是为什么你不能在常量表达式中使用字符串插值。
我想使用String Interpolation来Map命名参数(它们在字典中以Keys的形式存在)到它们各自的值。但是我不想使用String.Replace。有什么办法吗?
实际上,有几个模板引擎可以使用(主要用于日志记录),所以你不需要重新发明轮子。
也可以看看这个thread:What's a good way of doing string templating in .NET?

vu8f3i0k

vu8f3i0k3#

上面的答案并不完全正确,因为它们声称插值字符串只是string.Format()的语法糖,它们的能力只是稍微多一点。这使得使用FormattableString和自定义IFormatProvider/ICustomFormatter来实现您想要的功能成为可能,如下所示:

public class DictionaryFormatter : IFormatProvider, ICustomFormatter
{
    private Dictionary<string, string> _dict;
    public DictionaryFormatter(Dictionary<string, string> dict)
    {
        _dict = dict;
    }
    public string Format(string? format, object? arg, IFormatProvider? formatProvider)
    {
        var key = arg?.ToString() ?? String.Empty;
        return _dict[key];
    }

    public object? GetFormat(Type? formatType)
    {
        if (formatType == typeof(ICustomFormatter))
            return this;
        else
            return null;
    }
}

使用方法:

FormattableString x = $"This is an interpolated string with the first argument being: {"One"} and the second being {"Two"}";
var dict = new Dictionary<string, string>() {{"One", "First Value"}, {"Two", "Second Value"}};
var result = x.ToString(new DictionaryFormatter(dict));
Console.WriteLine(x.Format);
Console.WriteLine(string.Join(',', x.GetArguments()));
Console.WriteLine(result);

输出:

"This is an interpolated string with the first argument being {0} and the second being {1}"
"One,Two"
"This is an interpolated string with the first argument being First Value and the second being Second Value"

相关问题