动态字符串插值

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();
    });
}