I am currently testing Expression API and I am strugling to create an expression (using Expression API) similar to the following:
Expression<Func<string>> SomeFunction(string a)
{
return () => a;
}
My ideia is this function, instead of a "hardcoded" expression, would be using the Expression API. So far I got this working:
Expression<Func<string>> cache = null;
Expression<Func<string>> SomeFunction2(string a)
{
if(cache != null)
return cache;
var aVariable = Expression.Constant(a, typeof(string));
cache = Expression.Lambda<Func<string>>(aVariable);
return cache;
}
The problem with the above example is, since I captuire the variable as a constant, the second time I call this method, it will not work as expected. How can I create an expression using the scope variable? Is this kind of thing even possible?
In addition to my comment. If I udnerstood your problem correctly - try something like this:
ConcurrentDictionary<string, Expression<Func<string>>> cache = new(); // using concurrent one JIC, dependent on your usecase can be an overkill
public Expression<Func<string>> SomeFunction2(string a)
{
if(cache.TryGetValue(a, out var value))
return value;
var aVariable = Expression.Constant(a, typeof(string));
var res = Expression.Lambda<Func<string>>(aVariable);
cache.TryAdd(a, res);
return res;
}
Related
I am attempting to implement a method like:
(Func<T> getFn, Action<T> setFn) MakePair<T>(T initialVal) {
}
It will return two runtime generated lambdas that get and set a dynamically created variable using Expression trees to create the code.
My current solution is to dynamically create an array of the type with one element, and reference that:
(Func<T> getFn, Action<T> setFn) MakePair<T>(T initialVal) {
var dynvar = Array.CreateInstance(typeof(T), 1);
Expression<Func<Array>> f = () => dynvar;
var dynref = Expression.Convert(f.Body, typeof(T).MakeArrayType());
var e0 = Expression.Constant(0);
var getBody = Expression.ArrayIndex(dynref, e0);
var setParam = Expression.Parameter(typeof(T));
var setBody = Expression.Assign(Expression.ArrayAccess(dynref, e0), setParam);
var getFn = Expression.Lambda<Func<T>>(getBody).Compile();
var setFn = Expression.Lambda<Action<T>>(setBody, setParam).Compile();
return (getFn, setFn);
}
Is there a better way to create what may be a value type variable at runtime that can be read/written to than using an array?
Is there a better way to reference the runtime created array other than using a lambda to create the (field?) reference for use in the ArrayIndex/ArrayAccess method calls?
Excessive Background Info
For those that wonder, ultimately this came up in an attempt to create something like Perl auto-virification of lvalues for Perl hashes.
Imagine you have a List<T> with duplicate elements and want to create a Dictionary<T,int> that allows you to look up the count for each unique T in the list. You can use a few lines of code to count (in this case T is int):
var countDict = new Dictionary<int, int>();
foreach (var n in testarray) {
countDict.TryGetValue(n, out int c);
countDict[n] = c + 1;
}
But I want to do this with LINQ, and I want to avoid double-indexing countDict (interestingly, ConcurrentDictionary has AddOrUpdate for this purpose) so I use Aggregate:
var countDict = testarray.Aggregate(new Dictionary<int,int>(), (d, n) => { ++d[n]; return d; });
But this has a couple of issues. First, Dictionary won't create a value for a missing value, so you need a new type of Dictionary that auto-creates missing values using e.g. a seed lambda:
var countDict = testarray.Aggregate(new SeedDictionary<int, Ref<int>>(() => Ref.Of(() => 0)), (d, n) => { var r = d[n]; ++r.Value; return d; });
But you still have the lvalue problem, so you replace the plain int counter with a Ref class. Unfortunately, C# can't create a C++ first class Ref class, but using one based around auto-creating a setter lambda from a getter lambda (using expression trees) is close enough. (Unfortunately C# still won't accept ++d[n].Value; even though it should be valid, so you have to create a temporary.)
But now you have the problem of creating multiple runtime integer variables to hold the counts. I extended the Ref<> class to take a lambda that returns a constant (ConstantExpression) and create a runtime variable and build a getter and setter with the constant being the initial value.
I agree with some of the question commenters that expression trees seem unnecessary, so here is a simple implementation of the shown API without them:
struct Box<T> {
public T Value;
}
(Func<T> getFn, Action<T> setFn) MakePair<T>(T initialVal) {
var box = new Box<T> { Value = initialVal };
return (() => box.Value, v => box.Value = v);
}
As an answer to the stated question (how to define dynref without a lambda), then, is there something wrong with the following modifications to dynvar and dynref?
var dynvar = new T[] { initialVal };
var dynref = Expression.Constant(dynvar);
I'm using a combination of reflection and expression trees, and want to pass back certain property accessors from a class to a calling method. My current code has a method traversing the class and returning a list of MemberExpressions. The caller then iterates over the member expressions and creates lambdas, which should then be called with an instance of the inspected class to return the value of the property.
Here is a sample of what it would look like without the method calls (Runnable in LINQPad):
void Main()
{
var t = new Test { Prop = "Test" };
var property = t.GetType().GetProperty("Prop");
var baseType = Expression.Parameter(typeof(Test), "baseType");
var memberAccess = Expression.MakeMemberAccess(baseType, property);
var lambda = Expression.Lambda<Func<Test, string>>(memberAccess, Expression.Parameter(typeof(Test), "baseType"));
var func = lambda.Compile();
var result = func(t);
result.Dump();
}
class Test {
public string Prop { get; set; }
}
This does not work, throwing this exception:
InvalidOperationException: variable 'baseType' of type 'UserQuery+Test' referenced from scope '', but it is not defined
However, if I change the creation of the lambda to this:
var lambda = Expression.Lambda<Func<Test, string>>(memberAccess, baseType);
That is, replace the Expression.Parameter with the variable used earlier, then it works. This is not (easily) possible in the scenario where I want to use it, since I would have to return the original parameter along with the list (I could return a tuple, of course, but I would prefer not to, if it is not necessary).
Why does it work like this? Inspecting the DebugView of the lambda, they are exactly the same no matter what approach is used:
.Lambda #Lambda1<System.Func`2[UserQuery+Test,System.String]>(UserQuery+Test $baseType)
{
$baseType.S
}
Yes, you need to refer ParameterExpression, used earlier. This won't compile too:
private String Foo(Test myParam)
{
return myAnotherParam.MyProperty;
}
With instatiating new ParameterExpression in lambda, you're doing the same thing (but note, when making lambda, you're doing it in reversed order - first, you're constructing a method body, then - a method declaration):
// return myAnotherParam.MyProperty;
var baseType = Expression.Parameter(typeof(Test), "baseType");
var memberAccess = Expression.MakeMemberAccess(baseType, property);
// private String Foo(MyClass myParam)
var lambda = Expression.Lambda<Func<Test, string>>(memberAccess, Expression.Parameter(typeof(Test), "baseType"));
I'm writing a file generic block for my application and started with using Lambda expressions for managing my rule sets for block generation to avoid the pitfalls of magic strings, configuration hell etc.
Inside my mapping class I have lines similar to:
Map(x => x.Name).Length(20).PadLeft(true).PaddingChar("#");
This works fine and isn't where my question dwells, where I setup saving my information about the expression is in the Map method:
public override IPropertyMap Map(Expression<Func<T, object>> expression)
{
var propertyMap = new FixedLengthPropertyMap
{
//Length = 20,
//PaddingCharacter = " ",
PadLeft = false,
PropertyInfo = ReflectionHelper.GetProperty(expression)
};
_properties.Add(propertyMap);
return propertyMap;
}
_properties is just a List<IPropertyMap> that stores my info where my question from what is the best way to have a real object's data be read from the properties currently I came up with something similar to this:
var map = new AgentMap();
var agent = new Agent {Name = "Bob"};
string output = map.Write(agent);
public override string Write<T>(T agent)
{
var initial = _properties[0];
return initial.PropertyInfo.GetValue(agent, null) as string;
}
Is there a better way than using the GetValue method since earlier on I'm using an expression tree?
I don't see why you really need to use expression trees at all. Just make the Map method take a Func<T, object> and store that:
public override IPropertyMap Map(Func<T, string> fetcher)
{
var propertyMap = new FixedLengthPropertyMap
{
//Length = 20,
//PaddingCharacter = " ",
PadLeft = false,
Delegate = fetcher // Delegate is of type Delegate
};
_properties.Add(propertyMap);
return propertyMap;
}
Then:
public override string Write<T>(T agent)
{
var initial = _properties[0];
Func<T, string> fetcher = (Func<T, string>) initial.Delegate;
return fetcher(agent);
}
Is there any reason you particularly wanted to know the property and use an expression tree?
In part, it depends on what your scenario is. The "simple" answer is to just compile the expression and invoke it, but that has a potential performance impact if you are doing it in a tight loop (passing a delegate would be a lot quicker).
I'm not sure whether if would apply in this particular case (because of the agent), but to avoid doing too much expression compilation, you can look for simple scenarios and read the value directly from the expression tree; a little bit of PropertyInfo/FieldInfo is going to be quicker than compiling it...
For more, look at TryEvaluate here, and how it is used with Compile as a backup strategy (although you have the advantage of a known delegate type).
From my recent question, I try to centralize the domain model by including some silly logic in domain interface. However, I found some problem that need to include or exclude some properties from validating.
Basically, I can use expression tree like the following code. Nevertheless, I do not like it because I need to define local variable ("u") each time when I create lambda expression. Do you have any source code that is shorter than me? Moreover, I need some method to quickly access selected properties.
public void IncludeProperties<T>(params Expression<Func<IUser,object>>[] selectedProperties)
{
// some logic to store parameter
}
IncludeProperties<IUser>
(
u => u.ID,
u => u.LogOnName,
u => u.HashedPassword
);
Thanks,
Lambdas are great for many scenarios - but if you don't want them, perhaps simply don't use them? I hate to say it, but simple strings are tried and tested, especially for scenarios like data binding. If you want fast access, you could look at HyperDescriptor, or there are ways of compiling a delegate to the property accessors, or you can build an Expression from the string and compile it (including a cast to object if you want a known signature, rather than calling the (much slower) DynamicInvoke).
Of course, in most cases even crude reflection is fast enough, and isn't the bottleneck.
I suggest starting with the simplest code, and check it is actually too slow before worrying about it being fast. If it isn't too slow, don't change it. Any of the above options would work otherwise.
Another thought; if you are using Expression, you could do something like:
public void IncludeProperties<T>(
Expression<Func<T,object>> selectedProperties)
{
// some logic to store parameter
}
IncludeProperties<IUser>( u => new { u.ID, u.LogOnName, u.HashedPassword });
and then take the expression apart? A bit tidier, at least... here's some sample code showing the deconstruction:
public static void IncludeProperties<T>(
Expression<Func<T, object>> selectedProperties)
{
NewExpression ne = selectedProperties.Body as NewExpression;
if (ne == null) throw new InvalidOperationException(
"Object constructor expected");
foreach (Expression arg in ne.Arguments)
{
MemberExpression me = arg as MemberExpression;
if (me == null || me.Expression != selectedProperties.Parameters[0])
throw new InvalidOperationException(
"Object constructor argument should be a direct member");
Console.WriteLine("Accessing: " + me.Member.Name);
}
}
static void Main()
{
IncludeProperties<IUser>(u => new { u.ID, u.LogOnName, u.HashedPassword });
}
Once you know the MemberInfos (me.Member in the above), building your own lambdas for individual access should be trivial. For example (including a cast to object to get a single signature):
var param = Expression.Parameter(typeof(T), "x");
var memberAccess = Expression.MakeMemberAccess(param, me.Member);
var body = Expression.Convert(memberAccess, typeof(object));
var lambda = Expression.Lambda<Func<T, object>>(body, param);
var func = lambda.Compile();
Here's the shortest expression I can come up with:
public static void IncludeProperties(Expression<Action<IUser>> selectedProperties)
{
// some logic to store parameter
}
public static void S(params object[] props)
{
// dummy method to get to the params syntax
}
[Test]
public void ParamsTest()
{
IncludeProperties(u => S(
u.Id,
u.Name
));
}
I've tried to simplify this example, as the actual code I'm playing with is more complex. So while this example may seem silly, bear with me. Let's say I'm working with the AdventureWorks database and I decide I want to add a property called Blarg to the Product table that returns an expression that contains code I would like to use in several places:
public partial class Product
{
public Expression<Func<Product, string>> Blarg
{
get { return product => product.ProductModelID.HasValue ? "Blarg?" : "Blarg!"; }
}
}
What I want to do is create an expression expression tree, have it get the Expression from Product.Blarg, and group by the result. Something like this:
var productParameter = Expression.Parameter(typeof(Product), "product");
// The Problem
var groupExpression = Expression.Lambda<Func<Product, string>>(
Expression.Invoke(
Expression.Property(productParameter, "Blarg"),
productParameter),
productParameter);
using (AdventureWorksDataContext db = new AdventureWorksDataContext())
{
var result = db.Products.GroupBy(groupExpression).ToList();
// Throws ArgumentException: "The argument 'value' was the wrong type.
// Expected 'System.Delegate'.
// Actual 'System.Linq.Expressions.Expression`1[System.Func`2[LINQ_Test.Product,System.String]]'."
}
Obviously groupExpression is incorrect (see the code comment for the exception), but I'm not sure how I should be doing it. What I thought I was saying is "get the Expression from the product.Blarg, execute it, and return the string result." I guess that's not what I'm actually saying there, though. I'm still trying to figure out expression trees. Any idea how I could pull this off?
When you call Expression.Invoke, the first argument must be an existing LambdaExpression - it can't be an Expression to a LambdaExpression. Or in other words: it isn't going to evaluate Product.Blarg per row and use a different sub-expression each time.
Instead, you would retrieve this lambda first, perhaps making it static and accessing it via reflection if you only know it by name:
var lambda = (LambdaExpression) typeof(Product)
.GetProperty("Blarg").GetValue(null,null);
And pass lambda in as the argument to Expression.Invoke; here's a fully working LINQ-to-Objects example showing this (via AsQueryable()):
using System;
using System.Linq;
using System.Linq.Expressions;
public partial class Product
{
public static Expression<Func<Product, string>> Blarg
{
get { return product => product.ProductModelID.HasValue ? "Blarg?" : "Blarg!"; }
}
public int? ProductModelID { get; set; }
static void Main()
{
var lambda = (LambdaExpression)typeof(Product)
.GetProperty("Blarg").GetValue(null, null);
var productParameter = Expression.Parameter(typeof(Product), "product");
// The Problem
var groupExpression = Expression.Lambda<Func<Product, string>>(
Expression.Invoke(
lambda,
productParameter),
productParameter);
var data = new[] {
new Product { ProductModelID = 123},
new Product { ProductModelID = null},
new Product { ProductModelID = 456},
};
var qry = data.AsQueryable().GroupBy(groupExpression).ToList();
}
}
var qry = data.AsQueryable().GroupBy(Blarg).ToList();
That works, same as Marc's code.
Note: Blarg is already correct, there is no reason to 're-invoke' it.