4

For the following Lambda expression:

GetPropertyNameAndArrayIndex(() => SomeArray[0])

I know that you can get the property name for an expression. I also know that you can get the array index by using a ConstantExpression and accessing the Right value. My question is how do you get array index (or Right value) when it is not a constant, I.E.,

for (int i = 0; i < 5; i++)
{
    GetPropertyNameAndArrayIndex(() => SomeArray[i])
}

Any help would be greatly appreciated.

4
  • Please edit. A verb is missing in "I know that you can [missing verb] the property name and the array index " Commented Oct 5, 2009 at 20:56
  • @Emilio thanks for pointing out my poor grammar :) Commented Oct 5, 2009 at 21:05
  • 3
    Be careful with that snippet: the lambda will capture the i variable in a closre, not the value of the variable at the time it's created. You go call the function later and the expression created in all the iterations will evaluate as if they were created in the last iteration. Commented Oct 5, 2009 at 21:07
  • To clarify: I think you're probably okay here, because it looks like each iteration will call and use the lambda delegate before moving on to the next, rather than save it somewhere. But it's worth remembering. Commented Oct 5, 2009 at 21:09

1 Answer 1

3

As already noted in comments; note that the example given is susceptible to issues with captured variables if used asynchronously - but probably OK "as is".

To do it thoroughly involves a lot of edge-cases (or you can cheat and use Compile()) - but here's an example that shows the overall themes (without using Compile):

using System;
using System.Linq.Expressions;
using System.Reflection;
class Program
{
    static void Main()
    {
        string[] arr = { "abc", "def" };
        for (int i = 0; i < arr.Length; i++)
        {
            Foo(() => arr[i]);
        }
    }
    static object Foo<T>(Expression<Func<T>> lambda)
    {
        object obj = Walk(lambda.Body);
        Console.WriteLine("Value is: " + obj);
        return obj;

    }
    static object Walk(Expression expr)
    {
        switch (expr.NodeType)
        {
            case ExpressionType.ArrayIndex:

                BinaryExpression be = (BinaryExpression)expr;
                Array arr = (Array)Walk(be.Left);
                int index = (int) Walk(be.Right);
                Console.WriteLine("Index is: " + index);
                return arr.GetValue(index);
            case ExpressionType.MemberAccess:
                MemberExpression me = (MemberExpression)expr;
                switch (me.Member.MemberType)
                {
                    case MemberTypes.Property:
                        return ((PropertyInfo)me.Member).GetValue(Walk(me.Expression), null);
                    case MemberTypes.Field:
                        return ((FieldInfo)me.Member).GetValue(Walk(me.Expression));
                    default:
                        throw new NotSupportedException();
                }
            case ExpressionType.Constant:
                return ((ConstantExpression) expr).Value;
            default:
                throw new NotSupportedException();

        }
    }

}
Sign up to request clarification or add additional context in comments.

3 Comments

Is this an exhaustive list of walkables?
@Maslow absolutely not. I have a fuller 3.5 list somewhere, but this grows again in 4.0. The trick is to figure out what range of scenarios you need to support.
@Maslow - didn't we discuss that one by e-mail at some point? Sounds familiar.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.