我有一类允许的替换值:
class MaskDictionary
{
public int id { get; set; }
public string last { get; set; }
public string lastinitial { get; set; }
public string first { get; set; }
public string firstinitial { get; set; }
public string salutation { get; set; }
public DateTime today { get; set; }
}
我想将格式化字符串作为用户输入,例如:
string userFormat = "{last}, {first} {today}";
并生成插值。概念上类似于:
string.Format("{last}, {first} {today}", MaskDictionary);
但是使输入字符串动态化失败了:
string.Format(userFormat, MaskDictionary);
提供运行时格式化的简单、干净的方法是什么?
有一些笨拙的选项使用反射和递归替换,比如
string userFormat = "{last}, {first} {today}";
PropertyInfo[] properties = typeof(MaskDictionary).GetProperties();
foreach (PropertyInfo property in properties)
{
userFormat = string.Replace(property.name, property.GetValue(mask));
}
但必须有更好的方法。
--更新答案比较--
我测试了答案中提出的两个解决方案的性能,并得到了非常令人惊讶的结果。
static class Format2
{
static public string Format(string format, MaskDictionary md)
{
string val = format;
foreach (PropertyInfo property in typeof(MaskDictionary).GetProperties())
{
val = val.Replace("{" + property.Name + "}", property.GetValue(md).ToString());
}
return val;
}
}
static class Format1
{
public static string FormatWith(this string format, IFormatProvider provider, object source)
{
if (format == null)
throw new ArgumentNullException("format");
Regex r = new Regex(@"(?<start>\{)+(?<property>[\w\.\[\]]+)(?<format>:[^}]+)?(?<end>\})+",
RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
List<object> values = new List<object>();
string rewrittenFormat = r.Replace(format, delegate (Match m)
{
Group startGroup = m.Groups["start"];
Group propertyGroup = m.Groups["property"];
Group formatGroup = m.Groups["format"];
Group endGroup = m.Groups["end"];
values.Add((propertyGroup.Value == "0")
? source
: DataBinder.Eval(source, propertyGroup.Value));
return new string('{', startGroup.Captures.Count) + (values.Count - 1) + formatGroup.Value
+ new string('}', endGroup.Captures.Count);
});
return string.Format(provider, rewrittenFormat, values.ToArray());
}
}
Regex 解决方案比较慢,非常慢。使用具有 5 个属性字典对象和 105 个属性字典对象的短格式字符串(20 个字符,3 个替换)以及一个长格式字符串(2000 个字符,3 个替换)和一个长字典对象的 1000 次迭代,我得到以下结果:
短格式,小词典
正则表达式 - 2150 毫秒
替换 - 3 毫秒
格式短,字典大
正则表达式 - 2160 毫秒
替换 - 30 毫秒
长格式,短字典
正则表达式 - 2170 毫秒
替换 - 26 毫秒
长格式,大词典
正则表达式 - 2250 毫秒
替换 - 330 毫秒
Replace 不能很好地扩展到大字典,但它的启动速度要快得多,以至于需要大字典加上非常长的格式字符串才能变慢。使用 105 属性字典,大约需要 16,000 个字符格式字符串来处理相同数量的时间,~2500 毫秒。使用 5 属性小字典,正则表达式从未如此快。一个 600K 字符的格式字符串用于正则表达式需要 14000 毫秒,用于替换需要 7000 毫秒,而一个 1.7M 字符格式字符串需要 38000 毫秒与 21000 毫秒。只要字典对象的大小合理且格式字符串短于 80 页,替换就会获胜。
最佳答案
James Newton King(JSON 专家)使用定义的 FormatWith()
扩展方法 in this blog post这将基本上完成你正在尝试做的事情:
public static string FormatWith(this string format, IFormatProvider provider, object source)
{
if (format == null)
throw new ArgumentNullException("format");
Regex r = new Regex(@"(?<start>\{)+(?<property>[\w\.\[\]]+)(?<format>:[^}]+)?(?<end>\})+",
RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
List<object> values = new List<object>();
string rewrittenFormat = r.Replace(format, delegate(Match m)
{
Group startGroup = m.Groups["start"];
Group propertyGroup = m.Groups["property"];
Group formatGroup = m.Groups["format"];
Group endGroup = m.Groups["end"];
values.Add((propertyGroup.Value == "0")
? source
: DataBinder.Eval(source, propertyGroup.Value));
return new string('{', startGroup.Captures.Count) + (values.Count - 1) + formatGroup.Value
+ new string('}', endGroup.Captures.Count);
});
return string.Format(provider, rewrittenFormat, values.ToArray());
}
它基本上依赖正则表达式和 .NET Databinder处理执行实际匹配和替换的类。
关于c# - 如何在运行时使用 Interpolated [Named] 值获得类似 String.Format 的行为?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36437427/