Как получить имена свойств типа, используя лямбда-выражение и анонимный тип? - PullRequest
4 голосов
/ 29 марта 2012

Я пытаюсь использовать деревья выражений и анонимные типы для достижения следующих целей.

Допустим, у меня есть этот класс:

class Person
{
   public string FirstName {get;set;}
   public string MiddleName {get;set;}
   public string LastName {get;set;}
   public DateTime DateOfBirth {get;set;}
}

Теперь я хочу иметь возможность позвонить по следующему номеру:

string[] names = Foo<Person>(x=> new { x.LastName, x.DateOfBirth });

Я хочу, чтобы имена содержали 2 элемента: «LastName» и «DateOfBirth».

Я пытаюсь расширить PetaPoco безопасным для времени компиляции способом, а не писать строку sql, чтобы я мог указать список свойств / столбцов, которые я хочу включить в SQL, а не выбирая все. У меня есть довольно большие объекты, и есть случаи, когда я не хочу выбирать все столбцы по соображениям производительности.

Ответы [ 5 ]

3 голосов
/ 29 марта 2012

Попробуйте это для размера:

public static string[] Foo<T, TResult>(Expression<Func<T, TResult>> func)
{
    return typeof(TResult).GetProperties().Select(pi => pi.Name).ToArray();
}

Поскольку вы возвращаете анонимный тип из вашей lamda, вы можете перебрать все свойства этого анонимного типа и использовать предполагаемые имена свойств.Однако при использовании этого синтаксис будет больше похож на:

Foo((Person x) => new { x.LastName, x.DateOfBirth });

Это потому, что второй универсальный аргумент является аномальным типом.

2 голосов
/ 03 марта 2018

Ответы, данные здесь, работают, когда выбрано только одно свойство, ИЛИ, если выбрано несколько свойств.Ни один из них не работает для обоих. ответ от Lukazoid работает только для нескольких свойств, остальное для одного свойства - на момент написания моего ответа.

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

string[] Foo<T>(Expression<Func<Person, T>> func)
{
    if (func.Body is NewExpression)
    {
        // expression selects multiple properties, 
        // OR, single property but as an anonymous object

        // extract property names right from the expression itself
        return (func.Body as NewExpression).Members.Select(m => m.Name).ToArray();

        // Or, simply using reflection, as shown by Lukazoid
        // return typeof(T).GetProperties().Select(p => p.Name).ToArray();
    }
    else
    {
        // expression selects only a single property of Person,
        // and not as an anonymous object.
        return new string[] { (func.Body as MemberExpression).Member.Name };
    }        
}

Или, более кратко, используя троичный оператор, все становится просто так:

string[] Foo<T>(Expression<Func<Person, T>> func)
{
    return (func.Body as NewExpression) != null
        ? typeof(T).GetProperties().Select(p => p.Name).ToArray()
        : new string[] { (func.Body as MemberExpression).Member.Name };
}

Загрузить файл LinkPad: LinkPad
Просмотреть в Интернете: Repl.it

Пожалуйста, не стесняйтесь указывать на все, чтоВозможно, я пропустил.

2 голосов
/ 29 марта 2012

Мне лень, поэтому этот код обрабатывает только открытые свойства.Но это должно стать хорошей основой для начала работы.

public static string[] Foo<T>(Expression<Func<T, object>> func)
{
    var properties = func.Body.Type.GetProperties();

    return typeof(T).GetProperties()
        .Where(p => properties.Any(x => p.Name == x.Name))
        .Select(p =>
        {
            var attr = (ColumnAttribute) p.GetCustomAttributes(typeof(ColumnAttribute), true).FirstOrDefault();
            return (attr != null ? attr.Name : p.Name);
        }).ToArray();
}
0 голосов
/ 29 марта 2012

Полагаю, вам нужно разобрать код для Html.LabelFor(LabelExtensions.LabelFor<TModel,TValue> из System.Web.Mvc сборки).

Например, посмотрите на ExpressionHelper.GetExpressionText

Что касается замены имени элемента значением атрибута - вам придется использовать старомодное отражение.

0 голосов
/ 29 марта 2012

Страница кода - это тысяча слов, вот как Microsoft делает это в Prism :

///<summary>
/// Provides support for extracting property information based on a property expression.
///</summary>
public static class PropertySupport
{
    /// <summary>
    /// Extracts the property name from a property expression.
    /// </summary>
    /// <typeparam name="T">The object type containing the property specified in the expression.</typeparam>
    /// <param name="propertyExpression">The property expression (e.g. p => p.PropertyName)</param>
    /// <returns>The name of the property.</returns>
    /// <exception cref="ArgumentNullException">Thrown if the <paramref name="propertyExpression"/> is null.</exception>
    /// <exception cref="ArgumentException">Thrown when the expression is:<br/>
    ///     Not a <see cref="MemberExpression"/><br/>
    ///     The <see cref="MemberExpression"/> does not represent a property.<br/>
    ///     Or, the property is static.
    /// </exception>
    public static string ExtractPropertyName<T>(Expression<Func<T>> propertyExpression)
    {
        if (propertyExpression == null)
        {
            throw new ArgumentNullException("propertyExpression");
        }

        var memberExpression = propertyExpression.Body as MemberExpression;
        if (memberExpression == null)
        {
            throw new ArgumentException(Resources.PropertySupport_NotMemberAccessExpression_Exception, "propertyExpression");
        }

        var property = memberExpression.Member as PropertyInfo;
        if (property == null)
        {
            throw new ArgumentException(Resources.PropertySupport_ExpressionNotProperty_Exception, "propertyExpression");
        }

        var getMethod = property.GetGetMethod(true);
        if (getMethod.IsStatic)
        {
            throw new ArgumentException(Resources.PropertySupport_StaticExpression_Exception, "propertyExpression");
        }

        return memberExpression.Member.Name;
    }
}

Если вы хотите принять во внимание атрибуты, это будет немного сложнее, но общая идея принять Expression<Func<T>> и выловить название целевого свойства - то же самое.

Обновление: Как есть, метод будет принимать только один параметр; Я только предоставил это как руководство. Идея конечно может быть обобщена:

public static string[] ExtractPropertyNames<T>(
    Expression<Func<T, object>> propertyExpression)

Этот метод примет выражение, которое принимает T и возвращает анонимный тип, который вы можете затем отразить. Вы могли бы заменить параметр второго типа на object, но здесь он ничего не делает, потому что единственное, что вы хотите сделать, это отразить тип.

...