字符串表达式到 c# 函数委托
string expression to c# function delegate
我想将以下字符串转换为函数委托。
[Id]-[Description]
C#class:
public class Foo
{
public string Id {get;set;}
public string Description {get;set;}
}
结果函数委托:
Func<Foo, string> GetExpression = delegate()
{
return 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();
}
}
和用法:
var func = ParserCompiler.GetExpression<Foo>("[Id]-[Description]");
var formattedString = func(new 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);
}
灵感来自Generate lambda Expression By Clause using string.format in C#?.
A simple step by step version to create an Expression tree based on simple use case, can help in creating any kind of Expression tree
我们想要实现的目标:(在linqpad中编码,Dump是打印调用)
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();
Following is the Expression generated:
Step by Step process to Create an Expression Tree
Reviewing the Expression Tree,可以理解以下几点:
- 我们创建一个
typeof(Func<Foo,String>)
类型的 Func 委托
- 表达式的外部节点类型是
Lambda Type
- 只需要一个参数表达式
typeof(Foo)
- 在它需要的参数中,
MethodInfo
of string.Format
- 在 Format 方法的参数中,需要以下表达式
- a.) 常量表达式 -
{0}-{1}
- b.)
Id
字段的 MemberExpression
- c.)
Description
字段的 MemberExpression
- 中提琴和我们完成了
Using the Steps above following is the simple code to create Expression:
// 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"
我想将以下字符串转换为函数委托。
[Id]-[Description]
C#class:
public class Foo
{
public string Id {get;set;}
public string Description {get;set;}
}
结果函数委托:
Func<Foo, string> GetExpression = delegate()
{
return 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();
}
}
和用法:
var func = ParserCompiler.GetExpression<Foo>("[Id]-[Description]");
var formattedString = func(new 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);
}
灵感来自Generate lambda Expression By Clause using string.format in C#?.
A simple step by step version to create an Expression tree based on simple use case, can help in creating any kind of Expression tree
我们想要实现的目标:(在linqpad中编码,Dump是打印调用)
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();
Following is the Expression generated:
Step by Step process to Create an Expression Tree
Reviewing the Expression Tree,可以理解以下几点:
- 我们创建一个
typeof(Func<Foo,String>)
类型的 Func 委托
- 表达式的外部节点类型是
Lambda Type
- 只需要一个参数表达式
typeof(Foo)
- 在它需要的参数中,
MethodInfo
ofstring.Format
- 在 Format 方法的参数中,需要以下表达式
- a.) 常量表达式 -
{0}-{1}
- b.)
Id
字段的 MemberExpression - c.)
Description
字段的 MemberExpression - 中提琴和我们完成了
Using the Steps above following is the simple code to create Expression:
// 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"