Expression
- это дерево того, что компилируется компилятором C#. Вы должны использовать ExpressionVisitor
для извлечения информации Expression
Ваш базовый пример c не так уж тривиален. Вы объявляете переменную вне выражения и используете ее внутри. Это называется закрытием. Вы также используете поле вместо свойства, которое приведет к другому выражению.
class Program
{
static void Main(string[] args)
{
ClassToTest testClass = new ClassToTest();
new TestingClass<Int32>().RunTest(() => testClass.NumberOfCars);
}
}
Будет скомпилировано как
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>()));
}
}
Чтобы получить объект экземпляра без использования посетителя выражения, вы можете сделать:
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);
}
Но вы никогда не должны манипулировать дерево выражений, подобное этому. Всегда используйте ExpressionVisitor
и всегда понимайте, что вы посещаете.
Следующий посетитель - пример, который будет работать для вашего указанного c сценария.
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);
}
}
С помощью ExpressionVisitor
вы можете делать почти все, что вы хотите, с помощью выражения, но вам нужно хорошее понимание того, как все работает в компиляторе C#.