c#函数委托的字符串表达式

c#函数委托的字符串表达式,c#,lambda,expression-trees,func,C#,Lambda,Expression Trees,Func,我想将以下字符串转换为函数委托 [Id]-[Description] C级: 结果函数委托: Func<Foo, string> GetExpression = delegate() { return x => string.Format("{0}-{1}", x.Id, x.Description); }; Func GetExpression=delegate() { 返回x=>string.Format(“{0}-{1}”,x.Id,x.Description

我想将以下字符串转换为函数委托

[Id]-[Description]
C级:

结果函数委托:

Func<Foo, string> GetExpression = delegate()
{
    return x => string.Format("{0}-{1}", x.Id, x.Description);
};
Func GetExpression=delegate()
{
返回x=>string.Format(“{0}-{1}”,x.Id,x.Description);
};

我认为编译lambda或表达式解析器将是一种方法,但不确定最好的方法是什么。有任何输入吗?

可能是:构造Linq表达式,然后编译它。编译表达式是一个普通的委托,没有性能缺陷

编译时已知参数类型(
Foo
)时的实现示例:

class ParserCompiler
{
    private static (string format, IReadOnlyCollection<string> propertyNames) Parse(string text)
    {
        var regex = new Regex(@"(.*?)\[(.+?)\](.*)");

        var formatTemplate = new StringBuilder();
        var propertyNames = new List<string>();
        var restOfText = text;
        Match match;
        while ((match = regex.Match(restOfText)).Success)
        {
            formatTemplate.Append(match.Groups[1].Value);
            formatTemplate.Append("{");
            formatTemplate.Append(propertyNames.Count);
            formatTemplate.Append("}");

            propertyNames.Add(match.Groups[2].Value);

            restOfText = match.Groups[3].Value;
        }

        formatTemplate.Append(restOfText);

        return (formatTemplate.ToString(), propertyNames);
    }

    public static Func<T, string> GetExpression<T>(string text) //"[Id]-[Description]"
    {
        var parsed = Parse(text); //"{0}-{1}  Id, Description"

        var argumentExpression = Expression.Parameter(typeof(T));

        var properties = typeof(T)
            .GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetField)
            .ToDictionary(keySelector: propInfo => propInfo.Name);

        var formatParamsArrayExpr = Expression.NewArrayInit(
            typeof(object), 
            parsed.propertyNames.Select(propName => Expression.Property(argumentExpression, properties[propName])));

        var formatStaticMethod = typeof(string).GetMethod("Format", BindingFlags.Static | BindingFlags.Public, null,new[] { typeof(string), typeof(object[]) }, null);
        var formatExpr = Expression.Call(
            formatStaticMethod,
            Expression.Constant(parsed.format, typeof(string)),
            formatParamsArrayExpr);

        var resultExpr = Expression.Lambda<Func<T, string>>(
            formatExpr,
            argumentExpression); // Expression<Func<Foo, string>> a = (Foo x) => string.Format("{0}-{1}", x.Id, x.Description);

        return resultExpr.Compile();
    }
}
类解析器编译器
{
私有静态(字符串格式,IReadOnlyCollection属性名称)解析(字符串文本)
{
var regex=new regex(@“(.*?)\[(.+?)\](.*));
var formatTemplate=新的StringBuilder();
var propertyNames=新列表();
var restOfText=文本;
匹配;
while((match=regex.match(restOfText)).Success)
{
formatTemplate.Append(match.Groups[1].Value);
formatTemplate.Append(“{”);
Append(propertyNames.Count);
formatTemplate.Append(“}”);
propertyNames.Add(match.Groups[2].Value);
restOfText=match.Groups[3]。值;
}
Append(restOfText);
返回(formatTemplate.ToString(),propertyNames);
}
公共静态Func GetExpression(字符串文本)/“[Id]-[Description]”
{
var parsed=Parse(text);/“{0}-{1}Id,描述”
var argumentExpression=Expression.Parameter(typeof(T));
变量属性=类型(T)
.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetField)
.ToDictionary(键选择器:propInfo=>propInfo.Name);
var formatParamsArrayExpr=Expression.NewArrayInit(
类型(对象),
parsed.propertyNames.Select(propName=>Expression.Property(argumentExpression,properties[propName]);
var formatStaticMethod=typeof(string).GetMethod(“Format”,BindingFlags.Static | BindingFlags.Public,null,new[]{typeof(string),typeof(object[])null);
var formatExpr=Expression.Call(
格式化方法,
Expression.Constant(parsed.format,typeof(string)),
formatParamsArrayExpr);
var resultExpr=Expression.Lambda(
formatExpr,
argumentExpression);//表达式a=(foox)=>string.Format(“{0}-{1}”,x.Id,x.Description);
返回resultExpr.Compile();
}
}
使用方法:

        var func = ParserCompiler.GetExpression<Foo>("[Id]-[Description]");
        var formattedString = func(new Foo {Id = "id1", Description = "desc1"});
var func=ParserCompiler.GetExpression(“[Id]-[Description]”);
var formattedString=func(新的Foo{Id=“id1”,Description=“desc1”});

当我测试这个时,答案几乎相同,但是,由于下面的代码有一个优点,即最多调用格式化字符串中提到的每个属性一次,所以我还是要发布它:

public static Func<Foo, string> GetExpression(string query_string)
{
    (string format_string, List<string> prop_names) = QueryStringToFormatString(query_string);

    var lambda_parameter = Expression.Parameter(typeof(Foo));

    Expression[] formatting_params = prop_names.Select(
        p => Expression.MakeMemberAccess(lambda_parameter, typeof(Foo).GetProperty(p))
     ).ToArray();

    var formatMethod = typeof(string).GetMethod("Format", new[] { typeof(string), typeof(object[]) });

    var format_call = Expression.Call(formatMethod, Expression.Constant(format_string), Expression.NewArrayInit(typeof(object), formatting_params));

    var lambda = Expression.Lambda(format_call, lambda_parameter) as Expression<Func<Foo, string>>;
    return lambda.Compile();
}

// A *very* primitive parser, improve as needed
private static (string format_string, List<string> ordered_prop_names) QueryStringToFormatString(string query_string)
{
    List<string> prop_names = new List<string>();

    string format_string = Regex.Replace(query_string, @"\[.+?\]", m => {
        string prop_name = m.Value.Substring(1, m.Value.Length - 2);

        var known_pos = prop_names.IndexOf(prop_name);

        if (known_pos < 0)
        {
            prop_names.Add(prop_name);
            known_pos = prop_names.Count - 1;
        }

        return $"{{{known_pos}}}";
    });

    return (format_string, prop_names);
}
public static Func GetExpression(字符串查询\u字符串)
{
(字符串格式\u字符串,列表属性\u名称)=查询字符串到格式字符串(查询字符串);
var lambda_parameter=Expression.parameter(typeof(Foo));
表达式[]格式化参数=属性名称。选择(
p=>Expression.MakeMemberAccess(lambda_参数,typeof(Foo).GetProperty(p))
).ToArray();
var formatMethod=typeof(string).GetMethod(“Format”,new[]{typeof(string),typeof(object[])});
var format_call=Expression.call(formatMethod,Expression.Constant(format_string),Expression.NewArrayInit(typeof(object),formatting_params));
var lambda=Expression.lambda(格式调用,lambda参数)作为表达式;
返回lambda.Compile();
}
//一个*非常*原始的解析器,根据需要进行改进
私有静态(字符串格式\u字符串、列表顺序\u属性\u名称)查询字符串到格式字符串(字符串查询\u字符串)
{
列表属性名称=新列表();
字符串格式\u string=Regex.Replace(查询\u string,@“\[.+?\]”,m=>{
字符串prop_name=m.Value.Substring(1,m.Value.Length-2);
var known_pos=项目名称。IndexOf(项目名称);
如果(已知位置<0)
{
项目名称。添加(项目名称);
已知位置=道具名称。计数-1;
}
返回$“{{{known_pos}}}”;
});
返回(格式字符串、属性名称);
}
灵感来自于

基于简单用例创建表达式树的简单分步版本,有助于创建任何类型的表达式树

我们想要实现的:(在linqpad中编码,Dump是一个打印调用)


为什么不重写
ToString()
方法?@asd因为OP需要一个可以从其他地方执行的委托,通过传递相关输入,您有理由使用委托,而不仅仅是生成一个通用函数来解析字符串?@WiktorZychla我认为OP希望根据第一行中给出的示例字符串动态创建Func,但为什么您需要函数委托?编写一个简单的函数来实现这一点(例如,使用一堆
字符串。Replace
)非常简单。您需要函数的原因是什么?啊,是的,它只是查询每个属性的次数与格式字符串中提到的次数相同。。。我将要发布的一个调用每个属性一次;)谢谢这对于理解它是如何工作的非常有用,并且有助于轻松创建复杂的表达式。是的,虽然在反向解决问题时需要表达式树可视化工具,但Linqpad很好,还有其他类似的,但这是一种从头开始构建快乐编程的非常好的方法
public static Func<Foo, string> GetExpression(string query_string)
{
    (string format_string, List<string> prop_names) = QueryStringToFormatString(query_string);

    var lambda_parameter = Expression.Parameter(typeof(Foo));

    Expression[] formatting_params = prop_names.Select(
        p => Expression.MakeMemberAccess(lambda_parameter, typeof(Foo).GetProperty(p))
     ).ToArray();

    var formatMethod = typeof(string).GetMethod("Format", new[] { typeof(string), typeof(object[]) });

    var format_call = Expression.Call(formatMethod, Expression.Constant(format_string), Expression.NewArrayInit(typeof(object), formatting_params));

    var lambda = Expression.Lambda(format_call, lambda_parameter) as Expression<Func<Foo, string>>;
    return lambda.Compile();
}

// A *very* primitive parser, improve as needed
private static (string format_string, List<string> ordered_prop_names) QueryStringToFormatString(string query_string)
{
    List<string> prop_names = new List<string>();

    string format_string = Regex.Replace(query_string, @"\[.+?\]", m => {
        string prop_name = m.Value.Substring(1, m.Value.Length - 2);

        var known_pos = prop_names.IndexOf(prop_name);

        if (known_pos < 0)
        {
            prop_names.Add(prop_name);
            known_pos = prop_names.Count - 1;
        }

        return $"{{{known_pos}}}";
    });

    return (format_string, prop_names);
}
Expression<Func<Foo,string>> expression = (f) => string.Format($"{f.Id}- 
{f.Description}"); 

var foo = new Foo{Id = "1",Description="Test"};

var func  = expression.Compile();

func(foo).Dump(); // Result "1-Test"

expression.Dump();
// Create a ParameterExpression
var parameterExpression = Expression.Parameter(typeof(Foo),"f");

// Create a Constant Expression
var formatConstant  = Expression.Constant("{0}-{1}");

// Id MemberExpression
var idMemberAccess = Expression.MakeMemberAccess(parameterExpression, typeof(Foo).GetProperty("Id"));

// Description MemberExpression         
var descriptionMemberAccess = Expression.MakeMemberAccess(parameterExpression, typeof(Foo).GetProperty("Description"));

// String.Format (MethodCallExpression)
var formatMethod = Expression.Call(typeof(string),"Format",null,formatConstant,idMemberAccess,descriptionMemberAccess);

// Create Lambda Expression
var lambda = Expression.Lambda<Func<Foo,string>>(formatMethod,parameterExpression);

// Create Func delegate via Compilation
var func = lambda.Compile();

// Execute Delegate 
func(foo).Dump(); // Result "1-Test"