2

Is there any way to get the used instance from a linq expression as a reference or something else?

At the moment I have the following to declare my expression:

var testClass = new ClassToTest();
otherClass.RunTest(() => testClass.NumberOfCars);

Now I want to get the used instance object from the expression. So that I could later get another property dynamically for example. I know that I can just pass more parameters to the function itself to use them but I wonder if this is also possible by expression.

Update

public class ClassToTest
{
    public int NumberOfCars;
    public int NumberOfChildren;

    public ClassToTest()
    {
        NumberOfCars = 1;
        NumberOfChildren = 2;
    }
}

public class TestingClass<TResult>
{
    public bool RunTest(Expression<Func<TResult>> expression)
    {
        // var numberOfCars = get info with the help of the expression and reflection 
        // var instance = get used instance in expression
        if (instance.NumberOfChildren > 2 && numberOfCars == 1)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
}

var otherClass = new TestingClass<int>();

It is just a basic example to understand my problem. The example could be better solved to pass both values as a parameter and check them but I wondered if this is possible to archive so.

Dragonblf
  • 115
  • 1
  • 8
  • 1
    What if you don´t even have any instance? E.g. `(RunTest(() => 3)`. This question does not make much sense, as it assumes an expression allways represents an instance-method-call. But it could be **any** delegate. If you want to have such an expression, your delegate surely should also expect that instance as parameter. – MakePeaceGreatAgain Apr 06 '20 at 06:39
  • 1
    Anyway I can´t see where you use that expression. Why do you think you even need any? – MakePeaceGreatAgain Apr 06 '20 at 06:43

1 Answers1

1

An Expression is a tree of what is compiled by the C# compiler. You should use an ExpressionVisitor to extract information of Expression

Your basic example is not that trivial. You declare a variable outside the expression and use it inside. It is a called a closure. You also use field instead of property which will result in different expression.

class Program
{
    static void Main(string[] args)
    {
        ClassToTest testClass = new ClassToTest();
        new TestingClass<Int32>().RunTest(() => testClass.NumberOfCars);
    }
}

Will be compiled as

internal class Program
{
  [CompilerGenerated]
  private sealed class <>c__DisplayClass0_0
  {
    public ClassToTest testClass;
  }

  private static void Main(string[] args)
  {
    <>c__DisplayClass0_0 <>c__DisplayClass0_ = new <>c__DisplayClass0_0();
    <>c__DisplayClass0_.testClass = new ClassToTest();
    new TestingClass<int>().RunTest(
      Expression.Lambda<Func<int>>(
        Expression.Field(
          Expression.Field(
            Expression.Constant(<>c__DisplayClass0_, typeof(<>c__DisplayClass0_0)),
            "testClass", //FieldInfo.GetFieldFromHandle((RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/)),
          "NumberOfCars", //FieldInfo.GetFieldFromHandle((RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/)), 
         Array.Empty<ParameterExpression>()));
  }
}

To get the instance object without using an expression visitor you can do :

public bool RunTest(Expression<Func<TResult>> expression)
{
    // NumberOfCars
    var e1 = (MemberExpression)expression.Body;
    // testClass
    var e2 = (MemberExpression)e1.Expression;
    // closureObject 
    var e3 = (ConstantExpression)e2.Expression; 

    var closureObject = e3.Value;
    var testClassObject = ((FieldInfo)e2.Member).GetValue(closureObject);
    var numberOfCars = ((FieldInfo)e1.Member).GetValue(testClassObject);
 }

But you should never manipulate an expression tree like this. Always use an ExpressionVisitor and always understand what you are visiting.

The following visitor is an example that will work for your specific scenario.

public class XVisitor : ExpressionVisitor
{
    public static Object XVisit(Expression e)
    {
        XVisitor visitor = new XVisitor();
        visitor.Visit(e);

        return visitor._instance;
    }

    private Object _instance;

    protected override Expression VisitMember(MemberExpression node)
    {
        if (node.Expression.Type.GetCustomAttribute<CompilerGeneratedAttribute>() != null)
        {
            Object closureInstance = ((ConstantExpression)node.Expression).Value;
            this._instance = ((FieldInfo)node.Member).GetValue(closureInstance);
        }
        return base.VisitMember(node);
    }
}

With ExpressionVisitor you can do almost everything you want with expression but you need a good understanding on how things works within the C# compiler.

Cyril Durand
  • 15,834
  • 5
  • 54
  • 62