Дерево выражений: Получить значение свойства объекта Dynami c. - PullRequest
0 голосов
/ 04 марта 2020

У меня есть список динамических c объектов, где я хочу сделать запрос по пользовательскому свойству. Другими словами, это выглядело бы так, если бы я не собирался размышлять:

IEnumerable<User> FilterUsers(IEnumerable<User> users, string selectedValue)
{
    users.Where(user => user.Name == selectedValue);
}

До сих пор я придумал следующую реализацию, которая работает, если набрано users:

IEnumerable<User> FilterUsers(IEnumerable<User> users, string selectedField, string selectedValue)
{
    LabelTarget returnTarget = Expression.Label(typeof(bool));
    ParameterExpression userParameter = Expression.Parameter(typeof(User));
    MemberExpression userSelectedField = Expression.Property(userParameter, selectedField);
    Expression test = Expression.Equal(userSelectedField, Expression.Constant(selectedValue));
    Expression iftrue = Expression.Return(returnTarget, Expression.Constant(true));
    Expression iffalse = Expression.Return(returnTarget, Expression.Constant(false));

    var ex = Expression.Block(
        Expression.IfThenElse(test, iftrue, iffalse),
        Expression.Label(returnTarget, Expression.Constant(false)));

    var whereClause = Expression.Lambda<Func<User, bool>>(
        ex,
        new ParameterExpression[] { userParameter }
    ).Compile();

    return users.Where(user => whereClause(user));
}

Что я на самом деле пытаюсь сделать, так это сделать пользователей динамическими c объект:

IEnumerable<dynamic> FilterUsers(IEnumerable<dynamic> users, string selectedField, string selectedValue) {
    // ...
    ParameterExpression userParameter = Expression.Parameter(typeof(object)); // ???
    MemberExpression userSelectedField = Expression.Property(userParameter, selectedField); // throws
    // ...
}

Это вызывает следующее исключение: Instance property 'Name' is not defined for type 'System.Object' (Parameter 'propertyName'). Чего мне не хватает?

В качестве альтернативы, как я могу использовать Dictionary<string, object>?

Ответы [ 2 ]

1 голос
/ 04 марта 2020

Как @ canton7 сказал, вы должны использовать обобщенный c метод. Я также вижу в вашем вопросе, который вы указали, вы ищете свойства , почему бы не использовать обычное старое отражение?

public static IEnumerable<T> FilterItems<T>(IEnumerable<T> items, string property, string value)
{
  var prop = typeof(T).GetProperties().First(p => p.Name == property);      
  return items.Where(i => prop.GetValue(i).ToString().Contains(value));
}

Конечно, этот код должен быть улучшен для обработки различных ошибок .. ..

1 голос
/ 04 марта 2020

Использование dynamic здесь мало что дает: вам лучше использовать дженерики, если вы можете:

IEnumerable<T> FilterUsers<T>(IEnumerable<T> users, string selectedField, string selectedValue)
{
    var userParameter = Expression.Parameter(typeof(T));
    var userSelectedField = Expression.Property(userParameter, selectedField);
    // etc...
}

Если вам нужно использовать dynamic, тогда вам нужно получить тип времени выполнения каждого пользователя, используя .GetType(). Однако имейте в виду, что ничто не мешает кому-то передать IEnumerable, содержащий множество различных типов объектов, и им не обязательно иметь свойство с именем selectedField!

Или они могут передают множество различных типов объектов, каждый из которых имеет свойство selectedField, но они имеют разные свойства (например, class A { public string Foo { get; set; } } и class B { public string Foo { get; set; } } - эти два свойства Foo различны).

Таким образом, вам придется вызывать .GetType() для каждого из них, что означает, что вы не сможете получить преимущества в производительности от использования скомпилированных выражений.

Если вы можете гарантировать, что все элементы Если вы используете тот же тип, вы можете сделать что-то вроде:

private static IEnumerable<dynamic> FilterCollection(IEnumerable<dynamic> collection, string property, string value)
{
    if (!collection.Any()) return collection;
    var collectionItemType = collection.First().GetType();
    var userParameter = Expression.Parameter(typeof(object));
    var convertedUser = Expression.Convert(userParameter, collectionItemType);
    var userSelectedField = Expression.Property(convertedUser, selectedField);
    ...
}

Остерегайтесь, однако, вы перечисляете users дважды, что, вероятно, плохо. Вы могли бы лучше получить IEnumerator самостоятельно и работать с ним явно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...