Выражение, список констант, класс, сгенерированный компилятором

У меня есть этот простой код:

      public void MyWhere( Expression<Func<T, bool>> predicate)
{

}

List<string> Indexes2 = new List<string>();
Indexes2.Add("abc");
MyWhere(a=>Index2.Contains(a.a1));

При синтаксическом анализе выражения этот Index2 отображается как ConstantExpression. Затем, подобно многим примерам на этом сайте и в других местах, у меня есть этот метод для анализа значения ConatantExpression:

      private static object ConstantValue(ConstantExpression member)
{
    // source: http://stackoverflow.com/a/2616980/291955
    var objectMember = Expression.Convert(member, typeof(object));
    var getterLambda = Expression.Lambda<Func<object>>(objectMember);
    var getter = getterLambda.Compile();
    return getter();
}

Проблема в возвращаемом типе этого метода, тип возвращаемого значения:

{Name = "<>c__DisplayClass38_0" FullName = "S_Common.A_Dictionary`2+<>c__DisplayClass38_0[[S_Common.StringIndex, S_Common, Version=1.0.0.0, культура=нейтральная, PublicKeyToken=null],[DummyTestApp.test, DummyTestApp, Версия = 1.0.0.0, культура = нейтральная, PublicKeyToken = null]]"}

В QuickWatch можно найти лежащий в основе список, но почти нет возможности сослаться на него в коде.

1 ответ

Когда вы «закрываете» локальную переменную, создается скрытый класс. Что вы видите в ConstantExpressionявляется ссылкой на экземпляр этого скрытого класса.

Этот:

      public void MyWhere<T>(Expression<Func<T, bool>> predicate)
{
}

public void M() 
{
    List<string> Indexes2 = new List<string>();
    Indexes2.Add("abc");
    MyWhere<String>(a => Indexes2.Contains(a));
}

компилируется в

      [CompilerGenerated]
private sealed class <>c__DisplayClass1_0
{
    public List<string> Indexes2;
}

public void MyWhere<T>(Expression<Func<T, bool>> predicate)
{
}

public void M()
{
    <>c__DisplayClass1_0 <>c__DisplayClass1_ = new <>c__DisplayClass1_0();
    <>c__DisplayClass1_.Indexes2 = new List<string>();
    <>c__DisplayClass1_.Indexes2.Add("abc");
    ParameterExpression parameterExpression = Expression.Parameter(typeof(string), "a");
    MemberExpression instance = Expression.Field(Expression.Constant(<>c__DisplayClass1_, typeof(<>c__DisplayClass1_0)), FieldInfo.GetFieldFromHandle((RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/));
    MethodInfo method = (MethodInfo)MethodBase.GetMethodFromHandle((RuntimeMethodHandle)/*OpCode not supported: LdMemberToken*/, typeof(List<string>).TypeHandle);
    Expression[] array = new Expression[1];
    array[0] = parameterExpression;
    MethodCallExpression body = Expression.Call(instance, method, array);
    ParameterExpression[] array2 = new ParameterExpression[1];
    array2[0] = parameterExpression;
    MyWhere(Expression.Lambda<Func<string, bool>>(body, array2));
}

(см. Sharplab )

Интересными частями являются private sealed class <>c__DisplayClass1_0и Expression.Constant(<>c__DisplayClass1_, typeof(<>c__DisplayClass1_0)).

Этот скрытый класс скрыт. Взаимодействовать с ним можно только через отражение.

Ваша проблема на самом деле не решается простым способом. Для конкретного примера:

      public static void MyWhere<T>(Expression<Func<T, bool>> predicate)
{
    var body = predicate.Body;

    // .Contains(...)
    var contains = body as MethodCallExpression;

    // Indexes2
    var field = contains.Object;

    // Need boxing only for value types
    var boxIfNecessary = field.Type.IsValueType ? (Expression)Expression.Convert(field, typeof(object)) : field;
    var lambda = Expression.Lambda<Func<object>>(boxIfNecessary);
    var compiled = lambda.Compile();

    // Indexes of type List<string>()
    var value = compiled();
}

Например только это:

      MyWhere<string>(a => Enumerable.Contains(Indexes2, a));

сломает код, который я дал.

Другие вопросы по тегам