C# 如何使用lambda表达式和匿名类型获取类型的属性名?

C# 如何使用lambda表达式和匿名类型获取类型的属性名?,c#,linq,lambda,petapoco,C#,Linq,Lambda,Petapoco,我尝试使用表达式树和匿名类型来实现以下功能 假设我有一门课: 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<Perso

我尝试使用表达式树和匿名类型来实现以下功能

假设我有一门课:

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 });
string[]names=Foo(x=>new{x.LastName,x.DateOfBirth});
我希望名称包含两项,“姓氏”和“出生日期”


我试图以编译时安全的方式进行扩展,而不是编写字符串sql,这样我就可以指定要包含在sql中的属性/列的列表,而不是选择所有内容。我有一些相当大的实体,有些情况下,出于性能原因,我不想选择所有列。

一页代码就是千言万语,下面是Microsoft如何做到这一点的:

///
///提供基于属性表达式提取属性信息的支持。
///
公共静态类属性支持
{
/// 
///从属性表达式中提取属性名称。
/// 
///包含表达式中指定的属性的对象类型。
///属性表达式(例如p=>p.PropertyName)
///属性的名称。
///如果值为null,则引发。
///表达式为时引发:
///不是一个
///不代表属性。
///或者,属性是静态的。 /// 公共静态字符串ExtractPropertyName(表达式propertyExpression) { if(propertyExpression==null) { 抛出新ArgumentNullException(“propertyExpression”); } var memberExpression=propertyExpression.Body作为memberExpression; if(memberExpression==null) { 抛出新的ArgumentException(Resources.PropertySupport_NotMemberAccessExpression_异常,“propertyExpression”); } var property=memberExpression.Member作为PropertyInfo; if(属性==null) { 抛出新的ArgumentException(Resources.PropertySupport_ExpressionNotProperty_异常,“propertyExpression”); } var getMethod=property.getMethod(true); if(getMethod.IsStatic) { 抛出新的ArgumentException(Resources.PropertySupport_StaticExpression_异常,“propertyExpression”); } 返回memberExpression.Member.Name; } }
如果您想考虑属性,它会稍微复杂一些,但是接受
表达式
并找出目标属性的名称的一般思路是相同的

更新:按原样,该方法只接受一个参数;我只是提供了一个指南。这个想法当然可以概括为:

public static string[] ExtractPropertyNames<T>(
    Expression<Func<T, object>> propertyExpression)
公共静态字符串[]提取属性名称(
表达式属性(表达式)

此方法将接受一个表达式,该表达式接受一个T,并返回一个匿名类型,然后您可以对该类型进行反思。您可以用第二个类型参数替换
对象
,但这在这里并没有任何作用,因为您只想反映类型。

我想您必须从
System.Web.Mvc
汇编中反汇编
Html.LabelFor(LabelExtensions.LabelFor
)的代码

例如,查看
ExpressionHelper.GetExpressionText


至于用属性成员值替换成员名,你必须使用老式的反射。

我很懒,所以这段代码只处理公共属性。但这应该是一个很好的基础,让你开始

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();
}
公共静态字符串[]Foo(表达式func)
{
var properties=func.Body.Type.GetProperties();
返回typeof(T).GetProperties()
.Where(p=>properties.Any(x=>p.Name==x.Name))
.选择(p=>
{
var attr=(ColumnAttribute)p.GetCustomAttributes(typeof(ColumnAttribute),true.FirstOrDefault();
返回(attr!=null?attr.Name:p.Name);
}).ToArray();
}
请尝试以下尺寸:

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

这是因为第二个泛型参数是一种异常类型。

这里给出的答案在仅选择单个属性或选择多个属性时有效。没有一个对两者都有效。在写我的答案时,by只适用于多个属性,其余的适用于单个属性

下面的代码考虑了这两种情况,也就是说,您可以使用它来选择单个属性和多个属性。请注意,我没有在这里添加任何心智检查,所以请随意添加您自己的

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(表达式func)
{
if(funct.Body是NewExpression)
{
//表达式选择多个属性,
//或者,单个属性,但作为匿名对象
//从表达式本身提取属性名称权限
return(func.Body作为NewExpression.Members.Select(m=>m.Name).ToArray();
//或者,简单地使用反射,如Lukazoid所示
//返回typeof(T).GetProperties().Select(p=>p.Name).ToArray();
}
其他的
{
//表达式仅选择Person的单个属性,
//而不是作为匿名对象。
返回新字符串[]{(func.Body作为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 };
}
string[]Foo(表达式func)
{
返回(func.Body作为NewExpression)!=null
?typeof(T).GetProperties().Select(p=>p.Name).ToArray()
:新字符串[]{(func.Body作为MemberExpression).Member.Name};
}
下载链接板文件:
在线查看:


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

这不是只支持一个成员(即x=>x.LastName)吗?你是对的,它只支持单个成员。此外,单个成员不能作为匿名对象。看看我,它涵盖了两种情况,即单成员和多成员
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 };
}