How to use variable inside "Where" keyword in C# [duplicate] - c#

I have simple lambda as a string:
var str = "f => f.Substring(0, f.IndexOf(' '))";
which eventualy takes first word from the passed string.
What is the easiest way to compile/convert this peace of string into Func and make the following code to work.
Func<string, string> func = ...
var firstWord = func("Hello World");
Will Expression help me? Will appreciate working sample,
Thanks

Problem is that there is no strong typing support here, so you can't even utilize expression trees. However, DynamicLinq already does this, so you can easily get this working with a few modifications. Just create a few static methods of your own in the DynamicExpression class to add this functionality:
public static Expression<Func<T, S>> ParseLambda<T, S>(string expression)
{
string paramString = expression.Substring(0, expression.IndexOf("=>")).Trim();
string lambdaString = expression.Substring(expression.IndexOf("=>") + 2).Trim();
ParameterExpression param = Expression.Parameter(typeof(T), paramString);
return (Expression<Func<T,S>>)ParseLambda(new[] { param }, typeof(S), lambdaString, null);
}
public static LambdaExpression ParseLambda(string expression, Type returnType, params Type[] paramTypes)
{
string paramString = expression.Substring(0, expression.IndexOf("=>")).Trim("() ".ToCharArray());
string lambdaString = expression.Substring(expression.IndexOf("=>") + 2).Trim();
var paramList = paramString.Split(',');
if (paramList.Length != paramTypes.Length)
throw new ArgumentException("Specified number of lambda parameters do not match the number of parameter types!", "expression");
List<ParameterExpression> parameters = new List<ParameterExpression>();
for (int i = 0; i < paramList.Length; i++)
parameters.Add(Expression.Parameter(paramTypes[i], paramList[i].Trim()));
return ParseLambda(parameters.ToArray(), returnType, lambdaString, null);
}
Usage for both:
Func<string, string> func = DynamicExpression.ParseLambda<string, string>("f => f.Substring(0, f.IndexOf(\" \"))").Compile();
Func<string, int, string> otherFunc = ((Expression<Func<string, int, string>>)DynamicExpression.ParseLambda("(str, ind) => (ind * 100).ToString() + str")).Compile();
Edit: This has not been thoroughly tested, so you might want to make sure with some unit tests that these produce the correct results. I'm not doing anything overly complicated, just explicitly declaring the parameters to use in the public static LambdaExpression ParseLambda(ParameterExpression[] parameters, Type resultType, string expression, params object[] values) method.
Also fixed bug pointed out by #Mauro Destro by trimming the parameter name from paramList when creating the ParameterExpression.

You can use Roslyn scripting to do this. For example:
var str = "f => f.Substring(0, f.IndexOf(' '))";
var func = await CSharpScript.EvaluateAsync<Func<string, string>>(str);
var firstWord = func("Hello World");
This requires the Microsoft.CodeAnalysis.CSharp.Scripting package.

You will need to examine the string and construct an expression from the provided string. There is no easy or built in method to obtain a method from a string like this.
Good luck.

You might be interested in CodeDom technology. link
There is no ready parser there, though.

Related

Convert C# function call to a string

I would like to create a function that converts a "C# function call" to a string.
For example:
In my C# Project there is a public function, that can be called like this:
myTestClass.myTestFunction();
It's a function without arguments and returnvalue.
Now I would like to implement another function that accepts a C#
"Expression" as argument and converts the "Expression" to a string:
Expression<Func<???>> expr = () => myTestClass.myTestFunction();
string myString="";
myString=convertExpressionToString(expr);
myString should contain now "myTestClass.myTestFunction();"
It is important that the complete function call including the class
name is inside the string.
Any ideas how to solve this?
For this case you can simply write
private static string ConvertExpressionToString(LambdaExpression expr)
{
var methodCallExpr = expr.Body as MethodCallExpression;
if (methodCallExpr != null ) {
return methodCallExpr.Method.DeclaringType.Name + "." +
methodCallExpr.Method.Name + "();";
}
}
The general case is more complicated; however, this gives you an idea of where to start.
A more elaborate version prints parameters passed as constant expressions:
private static string ConvertExpressionToString(LambdaExpression expr)
{
var sb = new StringBuilder();
var methodCallExpr = expr.Body as MethodCallExpression;
sb.Append(methodCallExpr.Method.DeclaringType.Name)
.Append(".")
.Append(methodCallExpr.Method.Name)
.Append("(");
var arguments = methodCallExpr.Arguments;
for (int i = 0; i < arguments.Count; i++) {
if (i > 0) {
sb.Append(", ");
}
var constExpr = arguments[i] as ConstantExpression;
if (constExpr == null) {
sb.Append("<expr>");
} else {
sb.Append(constExpr.ToString());
}
}
sb.Append(");");
return sb.ToString();
}
It can cope with this expression:
Expression<Action> expr = () => myTestClass.myTestFunction(5, "hello");
However, since the parameters can be any valid expressions, it quickly becomes complicated to include other cases. And then there are out and ref parameters and optional parameters.
Just call ToString on the method's body.
string myString = expr.Body.ToString();
what you're trying to get is an action
Expression<Action> expr = () => myTestClass.myTestFunction();
MethodCallExpression mbr = (MethodCallExpression)expr.Body;
String methodName = mbr.Method.Name;
Assert.AreEqual(methodName, "myTestFunction");
MemberExpression me = (MemberExpression) mbr.Object;
String memberName = me.Member.Name;
Assert.AreEqual(methodName, "myTestClass");
String finalName = string.Format("{0}.{1}()", memberName, methodName);
Assert.AreEqual("myTestClass.myTestFunction()", finalName);
because you're accessing myTestClass via a closure you need to realize that you're accessing it via a Member Expression
so the body is a Method call. we get the method name from that, then we get the expression representing the object that we're calling the method on, we cast that to a member expression because that's what it is in this case and we get the name from the member on that member expression.

Convert string into func

I have simple lambda as a string:
var str = "f => f.Substring(0, f.IndexOf(' '))";
which eventualy takes first word from the passed string.
What is the easiest way to compile/convert this peace of string into Func and make the following code to work.
Func<string, string> func = ...
var firstWord = func("Hello World");
Will Expression help me? Will appreciate working sample,
Thanks
Problem is that there is no strong typing support here, so you can't even utilize expression trees. However, DynamicLinq already does this, so you can easily get this working with a few modifications. Just create a few static methods of your own in the DynamicExpression class to add this functionality:
public static Expression<Func<T, S>> ParseLambda<T, S>(string expression)
{
string paramString = expression.Substring(0, expression.IndexOf("=>")).Trim();
string lambdaString = expression.Substring(expression.IndexOf("=>") + 2).Trim();
ParameterExpression param = Expression.Parameter(typeof(T), paramString);
return (Expression<Func<T,S>>)ParseLambda(new[] { param }, typeof(S), lambdaString, null);
}
public static LambdaExpression ParseLambda(string expression, Type returnType, params Type[] paramTypes)
{
string paramString = expression.Substring(0, expression.IndexOf("=>")).Trim("() ".ToCharArray());
string lambdaString = expression.Substring(expression.IndexOf("=>") + 2).Trim();
var paramList = paramString.Split(',');
if (paramList.Length != paramTypes.Length)
throw new ArgumentException("Specified number of lambda parameters do not match the number of parameter types!", "expression");
List<ParameterExpression> parameters = new List<ParameterExpression>();
for (int i = 0; i < paramList.Length; i++)
parameters.Add(Expression.Parameter(paramTypes[i], paramList[i].Trim()));
return ParseLambda(parameters.ToArray(), returnType, lambdaString, null);
}
Usage for both:
Func<string, string> func = DynamicExpression.ParseLambda<string, string>("f => f.Substring(0, f.IndexOf(\" \"))").Compile();
Func<string, int, string> otherFunc = ((Expression<Func<string, int, string>>)DynamicExpression.ParseLambda("(str, ind) => (ind * 100).ToString() + str")).Compile();
Edit: This has not been thoroughly tested, so you might want to make sure with some unit tests that these produce the correct results. I'm not doing anything overly complicated, just explicitly declaring the parameters to use in the public static LambdaExpression ParseLambda(ParameterExpression[] parameters, Type resultType, string expression, params object[] values) method.
Also fixed bug pointed out by #Mauro Destro by trimming the parameter name from paramList when creating the ParameterExpression.
You can use Roslyn scripting to do this. For example:
var str = "f => f.Substring(0, f.IndexOf(' '))";
var func = await CSharpScript.EvaluateAsync<Func<string, string>>(str);
var firstWord = func("Hello World");
This requires the Microsoft.CodeAnalysis.CSharp.Scripting package.
You will need to examine the string and construct an expression from the provided string. There is no easy or built in method to obtain a method from a string like this.
Good luck.
You might be interested in CodeDom technology. link
There is no ready parser there, though.

How to set parameter of method using reflection

The following code is a helper I've created for a system which allows administrators to create their own queries on a database. It returns a lambda expression based on the method and value provided.
I am attempting to find a way to pass parameters to the method call - in this example I am using the StartsWith parameter of String, and attempting to set StringComparison.OrdinalIgnoreCase as a parameter.
There will be others parameters required too, depending on the type of the property specified. I'm hoping that understanding the method of supplying the string comparison property will enable me to add the rest later.
The underlying code works correctly, I just need it to be case-insensitive.
I have used this question as a guide, but the solution does not seem applicable here.
Here is the code:
public static class LambdaExpressionHelper<T> {
public static Expression<Func<T, bool>> Build(string propertyName, string method, string propertyValue) {
PropertyInfo propertyInfo = typeof(T).GetProperty(propertyName);
ParameterExpression e = Expression.Parameter(typeof(T), "e");
MemberExpression m = Expression.MakeMemberAccess(e, propertyInfo);
ConstantExpression c = Expression.Constant(propertyValue, m.Type);
MethodInfo mi = m.Type.GetMethod(method, new Type[] { m.Type }, );
// The below caused errors
//object classInstance = Activator.CreateInstance(typeof(T), null);
//object[] paramArray = new object[] { StringComparison.OrdinalIgnoreCase };
//mi.Invoke(classInstance, paramArray);
Expression call = Expression.Call(m, mi, c);
Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(call, e);
return lambda;
}
}
// External code:
var lambda = LambdaExpressionHelper<MailingListMember>.Build("EmailAddress", "StartsWith", "RoRy#");
Thanks for any help.
In the general case this should work:
public static Expression<Func<T, bool>>
Build(string propertyName, string method, params object[] args)
{
var propertyInfo = typeof(T).GetProperty(propertyName);
var e = Expression.Parameter(typeof(T), "e");
var m = Expression.MakeMemberAccess(e, propertyInfo);
var mi = m.Type.GetMethod(method, args.Select(a => a.GetType()).ToArray());
var c = args.Select(a => Expression.Constant(a, a.GetType())).ToArray();
Expression call = Expression.Call(m, mi, c);
Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(call, e);
return lambda;
}
The idea is that you accept any number of constant arguments, and use their types to select the appropriate overload when calling GetMethod. After that, you create the appropriate number of constant expressions from those arguments to pass to Expression.Call.
So with the above code, you could do:
var l1 = LambdaExpressionHelper<MailingListMember>.Build(
"EmailAddress", "StartsWith", "RoRy#");
var l2 = LambdaExpressionHelper<MailingListMember>.Build(
"EmailAddress", "StartsWith", "RoRy#", StringComparison.OrdinalIgnoreCase);
If you also need to get the value StringComparison.OrdinalIgnoreCase from the string "StringComparison.OrdinalIgnoreCase", I would factor this out into a separate method so that the interface of Build can remain generic. Exactly how to do it is covered in Getting Enum value via reflection (and I guess in lots of similar questions as well).
Note: I don't have convenient access to a compiler right now, so please excuse any mistakes.
I'm not sure i understand your question exactly, but hopefully this will help you on the way.
If you are using .NET 4.0 you can use the new keyword "dynamic" for much easier reflection code:
dynamic myDynamicObj = "Hello World!"; // or Activator.CreateInstance...
var doesIndeed = myDynamicObj.StartsWith("Hello", StringComparison.OrdinalIgnoreCase);
This does evaluate at run time so make sure spelling/case etc is correct.
Edit:
Assuming you always wanted to call methods with one String-arg returning bool, a possible solution would be to create a delegate type -
delegate bool CompareString(String str);
and than have the second argument of Build as that type:
Build(String .., CompareString cs, String ...)
But this does not work if you need to add extra arguments, as in the second arg of type StringComparison. A if/switch could be the answer there though, i.e if(CompareString is StartsWith)...
Sorry, not at a windows-computer so i can't test further.

How would I add type specific implementations to linq binary expressions?

I have a fairly simple expression parser that uses the Linq.Expression namespace.
Input is something like: (1+1), it finds the left and right constants, and converts the operator char to an ExpressionTypes enum value, creates the appropriate expression, compiles and executes.
I'd really like to be able to do string manipulations, (abc+def) would evaluate to abcdef for instance, however:
System.InvalidOperationException: The binary operator Add is not defined for the types 'System.String' and 'System.String'
How would I go about implementing this myself?
Something like the equavlant to an ExpressionTypes.Concat would be ideal.
You have to create the MethodCallExpression yourself, which in this case is for the static method string.Concat. (This is what the compiler does itself when you compile such code)
Here's some code that demonstrates using MethodInfo to extend the definition of the binary expression Add to concatenation of strings.
One example uses the existing Concat method in the String type.
The second example uses a custom Concat method in the Program type (the class this is all embedded in):
private MethodInfo ConcatMethod = typeof(string).GetMethod("Concat", new Type[] { typeof(string), typeof(string) });
private MethodInfo ConcatMethod2 = typeof(Program).GetMethod("Concat", new Type[] { typeof(string), typeof(int) });
public static string Concat(string obj1, int obj2)
{
return string.Concat(obj1, obj2.ToString());
}
private Expression SomeCode(string leftStr, string rightStr)
{
Expression left = Expression.Constant(leftStr);
Expression right = Expression.Constant(rightStr);
return Expression.Add(left, right, ConcatMethod);
}
private Expression SomeCode(string leftStr, int rightInt)
{
Expression left = Expression.Constant(leftStr);
Expression right = Expression.Constant(rightInt);
return Expression.Add(left, right, ConcatMethod2);
}
static void CheesyTest2()
{
Program foo = new Program();
Expression exp = foo.SomeCode("abc", "def");
LambdaExpression lambdaExpression = Expression.Lambda(exp);
Delegate func = lambdaExpression.Compile();
object result = func.DynamicInvoke();
Console.WriteLine(string.Format("Expression result: {0}", result));
exp = foo.SomeCode("abc", 42);
lambdaExpression = Expression.Lambda(exp);
func = lambdaExpression.Compile();
result = func.DynamicInvoke();
Console.WriteLine(string.Format("Expression2 result: {0}", result));
}
For custom operators it's actually a method call so you need to find the corresponding method and create the expression tree for calling that method. Haven't work much with expression trees so im affraid I can't give you a code sample but I hope this Helps none the less

Replacing parameters in a lambda expression

I had a part of code that takes in lambda expressions at runtime, which I can then compile and invoke.
Something thing;
Expression<Action<Something>> expression = (c => c.DoWork());
Delegate del = expression.Compile();
del.DynamicInvoke(thing);
In order to save execution time, I stored those compiled delegates in a cache, a Dictionary<String, Delegate> which the key is the lambda expression string.
cache.Add("(Something)c => c.DoWork()", del);
For exact same calls, it worked fine. However I realized that I could receive equivalent lambdas, such as "d => d.DoWork()", which I should actually use the same delegate for, and I wasn't.
This got me wondering if there was a clean way (read "not using String.Replace", I already did that as a temporary fix) to replace the elements in a lambda expression, like maybe replacing them by arg0 so that both
(c => c.DoWork()) and (d => d.DoWork())
are transformed and compared as (arg0 => arg0.DoWork()) by using something fuctionnally similar to injecting a Expression.Parameter(Type, Name) in a lambda.
Is that possible ? (Answers can include C#4.0)
I used strings, since it was the easisest way for me. You can't manually change the name of the parameter expression (it has the "Name" property, but it is read-only), so you must construct a new expression from pieces. What I did is created a "nameless" parameter (actually, it gets an autogenerated name in this case, which is "Param_0") and then created a new expression almost the same as the old one, but using the new parameter.
public static void Main()
{
String thing = "test";
Expression<Action<String>> expression = c => c.ToUpper();
Delegate del = expression.Compile();
del.DynamicInvoke(thing);
Dictionary<String, Delegate> cache = new Dictionary<String, Delegate>();
cache.Add(GenerateKey(expression), del);
Expression<Action<String>> expression1 = d => d.ToUpper();
var test = cache.ContainsKey(GenerateKey(expression1));
Console.WriteLine(test);
}
public static string GenerateKey(Expression<Action<String>> expr)
{
ParameterExpression newParam = Expression.Parameter(expr.Parameters[0].Type);
Expression newExprBody = Expression.Call(newParam, ((MethodCallExpression)expr.Body).Method);
Expression<Action<String>> newExpr = Expression.Lambda<Action<String>>(newExprBody, newParam);
return newExpr.ToString();
}
There's more to a lambda expression than just the text. Lambdas are re-written by the compiler into something much more complicated. For example, they may close over variables (which could include other delegates). This means that two lambdas could look exactly the same, but perform completely different actions.
So you might have luck caching your compiled expression, but you need to give them a more meaningful name for the key than just the text of the expression. Otherwise no amount of argument substitution will help you.

Categories