4

我正在尝试使用表达式树和匿名类型来实现以下目标。

假设我有这个课程:

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 中的属性/列的列表,而不是选择所有内容。我有一些相当大的实体,在某些情况下,出于性能原因我不想选择所有列。

4

5 回答 5

3

试试这个尺寸:

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 });

这是因为第二个泛型参数是匿名类型。

于 2012-03-28T22:28:48.433 回答
2

我很懒,所以这段代码只处理公共属性。但这应该是您入门的良好基础。

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();
}
于 2012-03-28T22:24:50.553 回答
2

当仅选择单个属性或选择多个属性时,此处给出的答案有效。它们都不适合两者。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

请随时指出我可能遗漏的任何内容。

于 2018-03-03T15:09:23.010 回答
0

一页代码是一千个单词,下面是微软在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但这在这里并没有真正做任何事情,因为您唯一想做的就是反映类型。

于 2012-03-28T22:16:39.797 回答
-1

我猜你必须Html.LabelFor(LabelExtensions.LabelFor<TModel,TValue>System.Web.Mvc汇编中反汇编代码)。

例如,看ExpressionHelper.GetExpressionText

至于用属性成员值替换成员名称 - 您必须使用老式反射。

于 2012-03-28T22:17:52.620 回答