Expression.MethodCallExpression передает MemberExpression в качестве параметра

Я пытаюсь создать общее выражение с вызовом метода на

Перечислимый.Содержит

Итак, в основном я хочу получить эту простую лямбду

x => collection.Contains(x.SomeProperty)

Мой код пока выглядит так:

ParameterExpression parameterExpression = Expression.Parameter(typeof(T), "x");
MemberExpression memberExpression = Expression.Property(parameterExpression, propertyName);

MethodCallExpression methodCall = Expression.Call(
    typeof(Enumerable),
    "Contains",
    new Type[] { typeof(Object) },
    Expression.Constant(new Object[] { 1, 2, 3 }),
    memberExpression
);

Но потом сбрасывает

InvalidOperationException: Ни один универсальный метод «Содержит» для типа «System.Linq.Enumerable» не совместим с предоставленными аргументами и аргументами type
. Не следует указывать аргументы типа, если метод
не является универсальным.

Если я просто передам paramterExpression, он отлично работает, но это не то, что я хочу.

Мой вопрос сейчас. Есть ли способ передать Memberexpression вызову contains mehtod?


person Sebastian Münster    schedule 19.11.2018    source источник


Ответы (1)


Это зависит от Type из memberExpression, что, в свою очередь, зависит от типа, к которому обращается свойство.

Например. следующие работы:

void Main()
{
    string propertyName = "ObjectProperty";
    ParameterExpression parameterExpression = Expression.Parameter(typeof(T), "x");
    MemberExpression memberExpression = Expression.Property(parameterExpression, propertyName);
    MethodCallExpression methodCall = Expression.Call(
        typeof(Enumerable),
        "Contains",
        new Type[] { typeof(object) },
        Expression.Constant(new Object[] { 1, 2, 3 }),
        memberExpression
    );

    Console.WriteLine(Expression.Lambda<Func<T, bool>>(methodCall, parameterExpression).Compile()(new T()));
}

public class T
{
    public object ObjectProperty => 2;
    public int IntProperty => 4;
}

Следующее не делает:

void Main()
{
    string propertyName = "IntProperty";
    ParameterExpression parameterExpression = Expression.Parameter(typeof(T), "x");
    MemberExpression memberExpression = Expression.Property(parameterExpression, propertyName);
    MethodCallExpression methodCall = Expression.Call(
        typeof(Enumerable),
        "Contains",
        new Type[] { typeof(object) },
        Expression.Constant(new Object[] { 1, 2, 3 }),
        memberExpression
    );

    Console.WriteLine(Expression.Lambda<Func<T, bool>>(methodCall, parameterExpression).Compile()(new T()));
}

public class T
{
    public object ObjectProperty => 2;
    public int IntProperty => 4;
}

Вы можете использовать new Type[] { memberExpression.Type } вместо new Type[] { typeof(object) }, чтобы код адаптировался к типу свойства, хотя вам также необходимо, чтобы тип выражения аргумента (в данном случае Constant(new Object[] {...})) совпадал.

Обратите внимание, что здесь вы можете выполнять неявные приведения только в том случае, если они являются ссылочными типами, производными от рассматриваемого типа (object), поэтому свойство, возвращающее string или Uri, будет подходящим (хотя, очевидно, всегда будет ложным при проверке того, содержится ли оно в массив 1, 2, 3), но свойство, которое вернуло int, не является преобразованием бокса, а не преобразованием ссылки.

person Jon Hanna    schedule 19.11.2018