Parse string to C# lambda Func
Is there a way to convert string representation of lambda to a lambda Func?
Func<Product, bool> func = Parse<Product, bool>("product => product.Name.Le开发者_开发问答ngth > 0");
I tried Dynamic LINQ but it doesn't work as expected - for example it doesn't expect lambda syntax =>.
Summary of answers:
- writing my own C# compiler - very funny
- firing up external compiler (like csc.exe) - very slow
- using DLINQ - as I said I don't see how it can parse lambda expressions
Why do I need this: because there's no way to pass lambdas to custom attributes like
[Secure(role => role.CanDoThis && role.AllowedCount > 5)]
So as a workaround I'd like to pass lambda as string: "role => role.CanDoThis && role.AllowedCount > 5". But seems like I'll have to use DLINQ like this: "CanDoThis && AllowedCount > 5" - since that's the syntax it understands. But my question was about true lambdas, I've already used DLINQ at the time of asking.
They are many lambda expression parsers available. Some of them are Lambda-Parser, Sprache
Sample Code:
Example1 : string concat and number calculate:
string code = "2.ToString()+(4*2)"; // C# code Func<string>
func = ExpressionParser.Compile<Func<string>>(code); // compile code
string result = func(); // result = "28"
You could parse the string and build up a lambda expression using the Expression class, essentially duplicating the function of the compiler.
I guess you have to resort to the CSharpCodeProvider. However, dealing with all possible local variable references might not be trivial. And how would you tell the CSharpCodeProvider about the type of the lambda parameter? I would probably create a template class looking like this:
class ExpressionContainer {
public Expression<Func<Product, bool>> TheExpression;
public string Length;
public ExpressionContainer() {
TheExpression = <user expression text>;
}
}
Then do something like this:
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);
Note, however, that for long-running applications, you should create all these in-memory assemblies in a separate appdomain since they can't be freed until the appdomain they reside in is unloaded.
You might be able to do something with CSharpCodeProvider (wrap the expression with some more code to create a valid class and compile it into an assembly, then load the assembly).
I believe that is how LINQPad does it.
Responding to your more specific problem, (and you may already know this but I'll try mentioning it anyway), you could create a dictionary that maps values that can be a constant (integers or enums) to lambdas.
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];
}
}
[Secure(SecureFuncType.Type1)]
sealed class TestClass {
}
// etc...
精彩评论