动态字符串插值
Dynamic string interpolation
谁能帮我解决这个问题?
要求的输出:“管理员的待办事项”
class Program
{
static void Main(string[] args)
{
Console.WriteLine(ReplaceMacro("{job.Name} job for admin", new Job { Id = 1, Name = "Todo", Description="Nothing" }));
Console.ReadLine();
}
static string ReplaceMacro(string value, Job job)
{
return value; //Output should be "Todo job for admin"
}
}
class Job
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
我真的不明白你ReplaceMacro
方法的意义...
但它应该是这样工作的:
class Program
{
static void Main(string[] args)
{
var job = new Job { Id = 1, Name = "Todo", Description = "Nothing" };
Console.WriteLine($"{job.Name} job for admin");
Console.ReadLine();
}
}
但是如果你真的想要动态的感觉,你的 ReplaceMacro
方法应该只接受一个参数,这就是工作:
static string ReplaceMacro(Job job)
{
return $"{job.Name} job for admin.";
}
并像这样使用它:
var job = new Job { Id = 1, Name = "Todo", Description = "Nothing" };
Console.WriteLine(ReplaceMacro(job));
或类似的东西。
您不能以这种方式使用字符串插值。但是您仍然可以使用 C#6 之前的方法来使用 string.Format
:
static void Main(string[] args)
{
Console.WriteLine(ReplaceMacro("{0} job for admin", new Job { Id = 1, Name = "Todo", Description = "Nothing" }));
Console.ReadLine();
}
static string ReplaceMacro(string value, Job job)
{
return string.Format(value, job.Name);
}
@ThePerplexedOne 的回答更好,但如果你真的需要避免字符串插值,那么
static string ReplaceMacro(string value, Job job)
{
return value?.Replace("{job.Name}", job.Name); //Output should be "Todo job for admin"
}
您应该将函数更改为:
static string ReplaceMacro(Job obj, Func<dynamic, string> function)
{
return function(obj);
}
并称它为:
Console.WriteLine(
ReplaceMacro(
new Job { Id = 1, Name = "Todo", Description = "Nothing" },
x => $"{x.Name} job for admin"));
两条建议:
DataBinder.Eval
string ReplaceMacro(string value, Job job)
{
return Regex.Replace(value, @"{(?<exp>[^}]+)}", match => {
return (System.Web.UI.DataBinder.Eval(new { Job = job }, match.Groups["exp"].Value) ?? "").ToString();
});
}
Linq.Expression
使用 MSDN LINQSamples 中提供的动态查询 class:
string ReplaceMacro(string value, Job job)
{
return Regex.Replace(value, @"{(?<exp>[^}]+)}", match => {
var p = Expression.Parameter(typeof(Job), "job");
var e = System.Linq.Dynamic.DynamicExpression.ParseLambda(new[] { p }, null, match.Groups["exp"].Value);
return (e.Compile().DynamicInvoke(job) ?? "").ToString();
});
}
在我看来,Linq.Expression更强大,所以如果你相信输入的字符串,你可以做更多有趣的事情,即:
value = "{job.Name.ToUpper()} job for admin"
return = "TODO job for admin"
您需要命名字符串格式替换。请参阅 Phil Haack 多年前的 post:http://haacked.com/archive/2009/01/04/fun-with-named-formats-string-parsing-and-edge-cases.aspx/
此通用解决方案扩展了
提供的答案
它可以用于任何类型的对象。
安装System.Linq.Dynamic
Install-Package System.Linq.Dynamic -Version 1.0.7
string ReplaceMacro(string value, object @object)
{
return Regex.Replace(value, @"{(.+?)}",
match => {
var p = Expression.Parameter(@object.GetType(), @object.GetType().Name);
var e = System.Linq.Dynamic.DynamicExpression.ParseLambda(new[] { p }, null, match.Groups[1].Value);
return (e.Compile().DynamicInvoke(@object) ?? "").ToString();
});
}
查看 working demo 客户类型
不完全是,但通过 bit tweek,我创建了仅支持字段 / 属性 的通用插值。
public static string Interpolate(this string template, params Expression<Func<object, string>>[] values)
{
string result = template;
values.ToList().ForEach(x =>
{
MemberExpression member = x.Body as MemberExpression;
string oldValue = $"{{{member.Member.Name}}}";
string newValue = x.Compile().Invoke(null).ToString();
result = result.Replace(oldValue, newValue);
}
);
return result;
}
测试用例
string jobStr = "{Name} job for admin";
var d = new { Id = 1, Name = "Todo", Description = "Nothing" };
var result = jobStr.Interpolate(x => d.Name);
另一个
string sourceString = "I wanted abc as {abc} and {dateTime} and {now}";
var abc = "abcIsABC";
var dateTime = DateTime.Now.Ticks.ToString();
var now = DateTime.Now.ToString();
string result = sourceString.Interpolate(x => abc, x => dateTime, x => now);
将字符串包装在一个函数中...
var f = x => $"Hi {x}";
f("Mum!");
//... Hi Mum!
您可以使用 RazorEngine:
using RazorEngine;
class Program
{
static void Main(string[] args)
{
Console.WriteLine(ReplaceMacro("@Model.Name job for admin", new Job { Id = 1, Name = "Todo", Description="Nothing" }));
Console.ReadLine();
}
static string ReplaceMacro(string value, Job job)
{
return Engine.Razor.RunCompile(value, "key", typeof(Job), job);
}
}
它甚至支持匿名类型和方法调用:
string template = "Hello @Model.Name. Today is @Model.Date.ToString(\"MM/dd/yyyy\")";
var model = new { Name = "Matt", Date = DateTime.Now };
string result = Engine.Razor.RunCompile(template, "key", null, model);
如果你真的需要这个,你可以使用 Roslyn 来创建字符串 – class 实现如
var stringToInterpolate = "$@\"{{job.Name}} job for admin\"";
var sourceCode = $@"
using System;
class RuntimeInterpolation(Job job)
{{
public static string Interpolate() =>
{stringToInterpolate};
}}";
然后组装
var assembly = CompileSourceRoslyn(sourceCode);
var type = assembly.GetType("RuntimeInterpolation");
var instance = Activator.CreateInstance(type);
var result = (string) type.InvokeMember("Interpolate",
BindingFlags.Default | BindingFlags.InvokeMethod, null, instance, new object[] {new Job { Id = 1, Name = "Todo", Description="Nothing" }});
Console.WriteLine(result);
您将在运行时获得此代码并获得结果(您还需要将 link 附加到您的作业 class 到该程序集)
using System;
class RuntimeInterpolation(Job job)
{
public static string Interpolate() =>
$@"{job.Name} job for admin";
}
您可以在此处阅读有关如何实施 CompileSourceRoslyn 的信息
派对来晚了!这是我写的-
using System.Reflection;
using System.Text.RegularExpressions;
public static class AmitFormat
{
//Regex to match keywords of the format {variable}
private static readonly Regex TextTemplateRegEx = new Regex(@"{(?<prop>\w+)}", RegexOptions.Compiled);
/// <summary>
/// Replaces all the items in the template string with format "{variable}" using the value from the data
/// </summary>
/// <param name="templateString">string template</param>
/// <param name="model">The data to fill into the template</param>
/// <returns></returns>
public static string FormatTemplate(this string templateString, object model)
{
if (model == null)
{
return templateString;
}
PropertyInfo[] properties = model.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
if (!properties.Any())
{
return templateString;
}
return TextTemplateRegEx.Replace(
templateString,
match =>
{
PropertyInfo property = properties.FirstOrDefault(propertyInfo =>
propertyInfo.Name.Equals(match.Groups["prop"].Value, StringComparison.OrdinalIgnoreCase));
if (property == null)
{
return string.Empty;
}
object value = property.GetValue(model, null);
return value == null ? string.Empty : value.ToString();
});
}
}
例子-
string format = "{foo} is a {bar} is a {baz} is a {qux} is a really big {fizzle}";
var data = new { foo = 123, bar = true, baz = "this is a test", qux = 123.45, fizzle = DateTime.UtcNow };
与 Phil Haack 给出的其他实现相比,这里是上述示例的结果 -
AmitFormat took 0.03732 ms
Hanselformat took 0.09482 ms
OskarFormat took 0.1294 ms
JamesFormat took 0.07936 ms
HenriFormat took 0.05024 ms
HaackFormat took 0.05914 ms
想知道没有人提到 mustache-sharp. Downloadable via Nuget。
string templateFromSomewhere = "url: {{Url}}, Name:{{Name}}";
FormatCompiler compiler = new FormatCompiler();
Generator generator = compiler.Compile(templateFromSomewhere);
string result = generator.Render(new
{
Url="https://google.com",
Name = "Bob",
});//"url: https://google.com, Name:Bob"
可以在单元测试文件中找到更多示例 here。
从接受的答案开始,我创建了一个通用的扩展方法:
public static string Replace<T>(this string template, T value)
{
return Regex.Replace(template, @"{(?<exp>[^}]+)}", match => {
var p = Expression.Parameter(typeof(T), typeof(T).Name);
var e = System.Linq.Dynamic.Core.DynamicExpressionParser.ParseLambda(new[] { p }, null, match.Groups["exp"].Value);
return (e.Compile().DynamicInvoke(value) ?? "").ToString();
});
}
谁能帮我解决这个问题?
要求的输出:“管理员的待办事项”
class Program
{
static void Main(string[] args)
{
Console.WriteLine(ReplaceMacro("{job.Name} job for admin", new Job { Id = 1, Name = "Todo", Description="Nothing" }));
Console.ReadLine();
}
static string ReplaceMacro(string value, Job job)
{
return value; //Output should be "Todo job for admin"
}
}
class Job
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
我真的不明白你ReplaceMacro
方法的意义...
但它应该是这样工作的:
class Program
{
static void Main(string[] args)
{
var job = new Job { Id = 1, Name = "Todo", Description = "Nothing" };
Console.WriteLine($"{job.Name} job for admin");
Console.ReadLine();
}
}
但是如果你真的想要动态的感觉,你的 ReplaceMacro
方法应该只接受一个参数,这就是工作:
static string ReplaceMacro(Job job)
{
return $"{job.Name} job for admin.";
}
并像这样使用它:
var job = new Job { Id = 1, Name = "Todo", Description = "Nothing" };
Console.WriteLine(ReplaceMacro(job));
或类似的东西。
您不能以这种方式使用字符串插值。但是您仍然可以使用 C#6 之前的方法来使用 string.Format
:
static void Main(string[] args)
{
Console.WriteLine(ReplaceMacro("{0} job for admin", new Job { Id = 1, Name = "Todo", Description = "Nothing" }));
Console.ReadLine();
}
static string ReplaceMacro(string value, Job job)
{
return string.Format(value, job.Name);
}
@ThePerplexedOne 的回答更好,但如果你真的需要避免字符串插值,那么
static string ReplaceMacro(string value, Job job)
{
return value?.Replace("{job.Name}", job.Name); //Output should be "Todo job for admin"
}
您应该将函数更改为:
static string ReplaceMacro(Job obj, Func<dynamic, string> function)
{
return function(obj);
}
并称它为:
Console.WriteLine(
ReplaceMacro(
new Job { Id = 1, Name = "Todo", Description = "Nothing" },
x => $"{x.Name} job for admin"));
两条建议:
DataBinder.Eval
string ReplaceMacro(string value, Job job)
{
return Regex.Replace(value, @"{(?<exp>[^}]+)}", match => {
return (System.Web.UI.DataBinder.Eval(new { Job = job }, match.Groups["exp"].Value) ?? "").ToString();
});
}
Linq.Expression
使用 MSDN LINQSamples 中提供的动态查询 class:
string ReplaceMacro(string value, Job job)
{
return Regex.Replace(value, @"{(?<exp>[^}]+)}", match => {
var p = Expression.Parameter(typeof(Job), "job");
var e = System.Linq.Dynamic.DynamicExpression.ParseLambda(new[] { p }, null, match.Groups["exp"].Value);
return (e.Compile().DynamicInvoke(job) ?? "").ToString();
});
}
在我看来,Linq.Expression更强大,所以如果你相信输入的字符串,你可以做更多有趣的事情,即:
value = "{job.Name.ToUpper()} job for admin"
return = "TODO job for admin"
您需要命名字符串格式替换。请参阅 Phil Haack 多年前的 post:http://haacked.com/archive/2009/01/04/fun-with-named-formats-string-parsing-and-edge-cases.aspx/
此通用解决方案扩展了
提供的答案
它可以用于任何类型的对象。
安装System.Linq.Dynamic
Install-Package System.Linq.Dynamic -Version 1.0.7
string ReplaceMacro(string value, object @object)
{
return Regex.Replace(value, @"{(.+?)}",
match => {
var p = Expression.Parameter(@object.GetType(), @object.GetType().Name);
var e = System.Linq.Dynamic.DynamicExpression.ParseLambda(new[] { p }, null, match.Groups[1].Value);
return (e.Compile().DynamicInvoke(@object) ?? "").ToString();
});
}
查看 working demo 客户类型
不完全是,但通过 bit tweek,我创建了仅支持字段 / 属性 的通用插值。
public static string Interpolate(this string template, params Expression<Func<object, string>>[] values)
{
string result = template;
values.ToList().ForEach(x =>
{
MemberExpression member = x.Body as MemberExpression;
string oldValue = $"{{{member.Member.Name}}}";
string newValue = x.Compile().Invoke(null).ToString();
result = result.Replace(oldValue, newValue);
}
);
return result;
}
测试用例
string jobStr = "{Name} job for admin";
var d = new { Id = 1, Name = "Todo", Description = "Nothing" };
var result = jobStr.Interpolate(x => d.Name);
另一个
string sourceString = "I wanted abc as {abc} and {dateTime} and {now}";
var abc = "abcIsABC";
var dateTime = DateTime.Now.Ticks.ToString();
var now = DateTime.Now.ToString();
string result = sourceString.Interpolate(x => abc, x => dateTime, x => now);
将字符串包装在一个函数中...
var f = x => $"Hi {x}";
f("Mum!");
//... Hi Mum!
您可以使用 RazorEngine:
using RazorEngine;
class Program
{
static void Main(string[] args)
{
Console.WriteLine(ReplaceMacro("@Model.Name job for admin", new Job { Id = 1, Name = "Todo", Description="Nothing" }));
Console.ReadLine();
}
static string ReplaceMacro(string value, Job job)
{
return Engine.Razor.RunCompile(value, "key", typeof(Job), job);
}
}
它甚至支持匿名类型和方法调用:
string template = "Hello @Model.Name. Today is @Model.Date.ToString(\"MM/dd/yyyy\")";
var model = new { Name = "Matt", Date = DateTime.Now };
string result = Engine.Razor.RunCompile(template, "key", null, model);
如果你真的需要这个,你可以使用 Roslyn 来创建字符串 – class 实现如
var stringToInterpolate = "$@\"{{job.Name}} job for admin\"";
var sourceCode = $@"
using System;
class RuntimeInterpolation(Job job)
{{
public static string Interpolate() =>
{stringToInterpolate};
}}";
然后组装
var assembly = CompileSourceRoslyn(sourceCode);
var type = assembly.GetType("RuntimeInterpolation");
var instance = Activator.CreateInstance(type);
var result = (string) type.InvokeMember("Interpolate",
BindingFlags.Default | BindingFlags.InvokeMethod, null, instance, new object[] {new Job { Id = 1, Name = "Todo", Description="Nothing" }});
Console.WriteLine(result);
您将在运行时获得此代码并获得结果(您还需要将 link 附加到您的作业 class 到该程序集)
using System;
class RuntimeInterpolation(Job job)
{
public static string Interpolate() =>
$@"{job.Name} job for admin";
}
您可以在此处阅读有关如何实施 CompileSourceRoslyn 的信息
派对来晚了!这是我写的-
using System.Reflection;
using System.Text.RegularExpressions;
public static class AmitFormat
{
//Regex to match keywords of the format {variable}
private static readonly Regex TextTemplateRegEx = new Regex(@"{(?<prop>\w+)}", RegexOptions.Compiled);
/// <summary>
/// Replaces all the items in the template string with format "{variable}" using the value from the data
/// </summary>
/// <param name="templateString">string template</param>
/// <param name="model">The data to fill into the template</param>
/// <returns></returns>
public static string FormatTemplate(this string templateString, object model)
{
if (model == null)
{
return templateString;
}
PropertyInfo[] properties = model.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
if (!properties.Any())
{
return templateString;
}
return TextTemplateRegEx.Replace(
templateString,
match =>
{
PropertyInfo property = properties.FirstOrDefault(propertyInfo =>
propertyInfo.Name.Equals(match.Groups["prop"].Value, StringComparison.OrdinalIgnoreCase));
if (property == null)
{
return string.Empty;
}
object value = property.GetValue(model, null);
return value == null ? string.Empty : value.ToString();
});
}
}
例子-
string format = "{foo} is a {bar} is a {baz} is a {qux} is a really big {fizzle}";
var data = new { foo = 123, bar = true, baz = "this is a test", qux = 123.45, fizzle = DateTime.UtcNow };
与 Phil Haack 给出的其他实现相比,这里是上述示例的结果 -
AmitFormat took 0.03732 ms
Hanselformat took 0.09482 ms
OskarFormat took 0.1294 ms
JamesFormat took 0.07936 ms
HenriFormat took 0.05024 ms
HaackFormat took 0.05914 ms
想知道没有人提到 mustache-sharp. Downloadable via Nuget。
string templateFromSomewhere = "url: {{Url}}, Name:{{Name}}";
FormatCompiler compiler = new FormatCompiler();
Generator generator = compiler.Compile(templateFromSomewhere);
string result = generator.Render(new
{
Url="https://google.com",
Name = "Bob",
});//"url: https://google.com, Name:Bob"
可以在单元测试文件中找到更多示例 here。
从接受的答案开始,我创建了一个通用的扩展方法:
public static string Replace<T>(this string template, T value)
{
return Regex.Replace(template, @"{(?<exp>[^}]+)}", match => {
var p = Expression.Parameter(typeof(T), typeof(T).Name);
var e = System.Linq.Dynamic.Core.DynamicExpressionParser.ParseLambda(new[] { p }, null, match.Groups["exp"].Value);
return (e.Compile().DynamicInvoke(value) ?? "").ToString();
});
}