Expression.MethodCallExpression передать MemberExpression в качестве параметра - PullRequest
0 голосов
/ 19 ноября 2018

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

Enumerable.Contains

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

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: универсальный метод 'Contains' для типа 'System.Linq.Enumerable' не совместим с предоставленным типом
аргументы и аргументы. Аргументы типа не должны предоставляться, если
метод не является универсальным

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

Мой вопрос сейчас. Есть ли способ передать Memberexression в вызов метода Contains?

1 Ответ

0 голосов
/ 19 ноября 2018

Это зависит от 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;
}

not:

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, не является таковым, как преобразование бокса, а не приведение ссылки.

...