【问题标题】:C# Linq Expression - How to get the instance of an expressionC# Linq 表达式 - 如何获取表达式的实例
【发布时间】:2023-04-06 05:24:01
【问题描述】:

有没有办法从 linq 表达式中获取使用的实例作为参考或其他东西?

目前我有以下声明我的表达式:

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

现在我想从表达式中获取使用的实例对象。这样我以后就可以动态地获取另一个属性,例如。我知道我可以将更多参数传递给函数本身以使用它们,但我想知道这是否也可以通过表达式来实现。

更新

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>();

这只是理解我的问题的一个基本示例。该示例可以更好地解决将两个值作为参数传递并检查它们,但我想知道这是否可以存档。

【问题讨论】:

  • 如果您甚至没有任何实例怎么办?例如。 (RunTest(() =&gt; 3)。这个问题没有多大意义,因为它假设一个表达式总是代表一个实例方法调用。但它可以是 any 代表。如果您想拥有这样的表达式,您的委托当然也应该期望该实例作为参数。
  • 无论如何我看不出你在哪里使用那个表达。为什么你认为你甚至需要任何东西?

标签: c# linq reflection linq-expressions


【解决方案1】:

Expression 是由 C# 编译器编译的树。您应该使用ExpressionVisitor 来提取Expression 的信息

您的基本示例并非微不足道。您在表达式外部声明一个变量并在内部使用它。它被称为闭包。您还使用字段而不是属性,这将导致不同的表达式。

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 并始终了解您正在访问的内容。

以下访问者是适用于您的特定场景的示例。

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# 编译器中的工作原理。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-03
    • 2019-07-12
    • 2011-07-02
    • 1970-01-01
    相关资源
    最近更新 更多