将字符串解析为C#lambda Func
有没有办法将lambda的字符串表示形式转换为lambda Func将字符串解析为C#lambda Func,c#,linq,C#,Linq,有没有办法将lambda的字符串表示形式转换为lambda Func Func<Product, bool> func = Parse<Product, bool>("product => product.Name.Length > 0"); 因此,作为一种解决方法,我希望将lambda作为字符串传递:“role=>role.CanDoThis&&role.AllowedCount>5”。但似乎我必须像这样使用DLINQ:“CanDoThis&&Allowe
Func<Product, bool> func = Parse<Product, bool>("product => product.Name.Length > 0");
因此,作为一种解决方法,我希望将lambda作为字符串传递:“role=>role.CanDoThis&&role.AllowedCount>5”。但似乎我必须像这样使用DLINQ:“CanDoThis&&AllowedCount>5”-因为这是它理解的语法。但我的问题是关于真正的lambdas,我在提问时已经使用了DLINQ。您可以解析字符串并使用该类构建lambda表达式,本质上复制编译器的功能。您可以使用(用更多代码包装表达式,以创建有效类并将其编译成程序集,然后加载程序集)
我相信这就是原因。我想您必须求助于CSharpCodeProvider。但是,处理所有可能的局部变量引用可能不是一件小事。您将如何告诉CSharpCodeProvider lambda参数的类型?我可能会创建一个如下所示的模板类:
class ExpressionContainer {
public Expression<Func<Product, bool>> TheExpression;
public string Length;
public ExpressionContainer() {
TheExpression = <user expression text>;
}
}
string source = <Code from above>;
Assembly a;
using (CSharpCodeProvider provider = new CSharpCodeProvider(...) {
List<string> assemblies = new List<string>();
foreach (Assembly x in AppDomain.CurrentDomain.GetAssemblies()) {
try {
assemblies.Add(x.Location);
}
catch (NotSupportedException) {
// Dynamic assemblies will throw, and in .net 3.5 there seems to be no way of finding out whether the assembly is dynamic before trying.
}
}
CompilerResults r = provider.CompileAssemblyFromSource(new CompilerParameters(assemblies.ToArray()) { GenerateExecutable = false, GenerateInMemory = true }, source);
if (r.Errors.HasErrors)
throw new Exception("Errors compiling expression: " + string.Join(Environment.NewLine, r.Errors.OfType<CompilerError>().Select(e => e.ErrorText).ToArray()));
a = r.CompiledAssembly;
}
object o = a.CreateInstance("ExpressionContainer");
var result = ( Expression<Func<Product, bool>>)o.GetType().GetProperty("TheExpression").GetValue(o);
class ExpressionContainer{
公开表达;
公共字符串长度;
public ExpressionContainer(){
表达式=;
}
}
然后像这样做:
class ExpressionContainer {
public Expression<Func<Product, bool>> TheExpression;
public string Length;
public ExpressionContainer() {
TheExpression = <user expression text>;
}
}
string source = <Code from above>;
Assembly a;
using (CSharpCodeProvider provider = new CSharpCodeProvider(...) {
List<string> assemblies = new List<string>();
foreach (Assembly x in AppDomain.CurrentDomain.GetAssemblies()) {
try {
assemblies.Add(x.Location);
}
catch (NotSupportedException) {
// Dynamic assemblies will throw, and in .net 3.5 there seems to be no way of finding out whether the assembly is dynamic before trying.
}
}
CompilerResults r = provider.CompileAssemblyFromSource(new CompilerParameters(assemblies.ToArray()) { GenerateExecutable = false, GenerateInMemory = true }, source);
if (r.Errors.HasErrors)
throw new Exception("Errors compiling expression: " + string.Join(Environment.NewLine, r.Errors.OfType<CompilerError>().Select(e => e.ErrorText).ToArray()));
a = r.CompiledAssembly;
}
object o = a.CreateInstance("ExpressionContainer");
var result = ( Expression<Func<Product, bool>>)o.GetType().GetProperty("TheExpression").GetValue(o);
字符串源=;
大会a;
使用(CSharpCodeProvider provider=新的CSharpCodeProvider(…){
列表程序集=新列表();
foreach(AppDomain.CurrentDomain.GetAssemblys()中的程序集x){
试一试{
组件。添加(x.位置);
}
捕获(不支持异常){
//动态程序集将抛出,在.NET3.5中,在尝试之前似乎无法确定程序集是否是动态的。
}
}
CompilerResults r=provider.CompileAsemblyFromSource(新编译器参数(assemblies.ToArray()){GenerateExecutable=false,GenerateInMemory=true},source);
if(r.Errors.hasrerrors)
抛出新异常(“编译表达式时出错:”+string.Join(Environment.NewLine,r.Errors.OfType().Select(e=>e.ErrorText.ToArray());
a=r.编译组件;
}
对象o=a.CreateInstance(“ExpressionContainer”);
var result=(表达式)o.GetType().GetProperty(“表达式”).GetValue(o);
但是,请注意,对于长时间运行的应用程序,您应该在单独的appdomain中创建所有这些内存中的程序集,因为只有卸载它们所在的appdomain才能释放它们。它们有许多可用的lambda表达式解析器。其中一些是 示例代码: 示例1:字符串concat和数字计算:
string code = "2.ToString()+(4*2)"; // C# code Func<string>
func = ExpressionParser.Compile<Func<string>>(code); // compile code
string result = func(); // result = "28"
string code=“2.ToString()+(4*2)”;//C#code Func
func=ExpressionParser.Compile(code);//编译代码
字符串result=func();//result=“28”
针对您更具体的问题,(您可能已经知道这一点,但我还是会尝试提及),您可以创建一个字典,将常量值(整数或枚举)映射到lambda
sealed class Product {
public bool CanDoThis { get; set; }
public int AllowedCount { get; set; }
}
public enum SecureFuncType {
Type1,
Type2,
Type3
}
sealed class SecureAttribute : Attribute {
[NotNull] readonly Func<Product, bool> mFunc;
public SecureAttribute(SecureFuncType pType) {
var secureFuncs = new Dictionary<SecureFuncType, Func<Product, bool>> {
{ SecureFuncType.Type1, role => role.CanDoThis && role.AllowedCount > 1 },
{ SecureFuncType.Type2, role => role.CanDoThis && role.AllowedCount > 2 },
{ SecureFuncType.Type3, role => role.CanDoThis && role.AllowedCount > 3 }
};
mFunc = secureFuncs[pType];
}
}
我知道如何调用csc.exe,并且有使用CodeDom进行动态编译的经验,这太多开销了-根据我的经验,它实际上运行csc.exe(当我在.NET 1.1上使用它时)。我想花时间在它上可能会很有趣,但对我的客户来说不是这样的-他们不付我钱来编写C#编译器。多么奇怪的客户啊。:)你为什么担心启动编译器会很慢?你可以缓存生成的表达式。看起来C#5会带来一些你想要的东西。看一看PDC 2008的视频,Anders Hejlsberg在视频中谈到了C#的未来。我正在等待C#4.0发布……C#5离我们太远了;-)实际上,我需要在属性中为lambdas提供此功能。希望4.0会有它(以及通用属性)。在c#4.0中不会有通用属性。遗憾的是,对于这种情况,我需要能够将lambda/delegate传递给属性,而不是泛型属性。参考问题中的4.0规范让我感到害怕;-)它甚至不包含“lambda”这个词!是的,然后我会让csc.exe运行,AppDomains只运行几个lambda。。。这让我想起了我的Turbo Pascal程序,它允许用户输入表达式。。。并且必须与Turbo Pascal编译器一起部署;-)另一方面,有一种叫做“动态方法”的方法。这是一种更轻量级的处理此类情况的方法。不幸的是,我从未使用过它们/@女王:这有关系吗?您的客户已经部署了csc,因为它是框架的一部分。另外,在单独的appdomain中运行的全部目的是,您可以检索值,然后拆下appdomain。您的客户可以购买相当多的新服务器来处理计算,而不用自己实现编译器,这样可以节省成本。