如何为类的每个属性创建lambda表达式

时间:2018-10-29 13:44:40

标签: c# lambda reflection expression

我有以下问题。我必须遍历该类的所有属性以配置一些生成器。类具有很多属性,因此代码很繁琐。看起来像这样:

var b = builder<MyTypeWith1000Properties>
    .WithProperty(x=>x.Property1)
    .WithProperty(x=>x.Property2)
    ...
    .WithProperty(x=>x.Property1000);

在许多地方,对于许多不同的类型,不仅在MyTypeWith1000Properties,而且还在重复代码。我正在考虑创建一些扩展名,例如:

var b = builder<MyTypeWith1000Properties>
    .WithAllProperties();

然后在WithAllProperties中,我可以使用Reflection遍历类型属性,如下所示:

public static IDataExtractor<T> WithAllProperties(this IDataExtractor<T> extractor)
{
    var properties = typeof(T).GetProperties();
    foreach (var property in properties)
    {
        extractor = extractor.WithProperty(/*the problem is here/*);
    }
    return extractor;
}

如何将循环中的属性变量转换为相应的表达式

Expression<Func<TRow, TValue>> propertyExpression

这是WithProperty期望的

2 个答案:

答案 0 :(得分:0)

更新-在Expression.Lambda中设置正确的参数值>

您可以尝试这样的事情

public static class BuilderExtension
{
    public static IDataExtractor<T> WithAllProperties<T>(this IDataExtractor<T> extractor)
    {
        var properties = typeof(T).GetProperties();
        foreach (var propertyInfo in properties)
        {
            var parameter = Expression.Parameter(typeof(T), "x");
            var property = Expression.Property(parameter, propertyInfo);
            var lambda = Expression.Lambda<Func<T, object>>(property,parameter);
            extractor = extractor.WithProperty(lambda);
        }
        return extractor;
    }
}

假设您具有以下类结构

public class MyTypeWith100Properties
{
    public string Property1 { get; set; }
    public string Property2 { get; set; }
    public string Property3 { get; set; }
    public string Property4 { get; set; }
}


public interface IDataExtractor<T>
{
    IDataExtractor<T> WithProperty(Expression<Func<T, object>> expr);
}

public class DataExtractor<T> : IDataExtractor<T>
{
    public List<Expression<Func<T, object>>> Expressions { get; private set; }

    public DataExtractor() {
        Expressions = new List<Expression<Func<T, object>>>();
    }
    public IDataExtractor<T> WithProperty(Expression<Func<T, object>> expr)
    {
        Expressions.Add(expr);
        return this;
    }
}

然后,如果您运行此

var builder = new DataExtractor<MyTypeWith100Properties>();
var b = builder.WithAllProperties<MyTypeWith100Properties>() 
                         as DataExtractor<MyTypeWith100Properties>;

var instance = new MyTypeWith100Properties() {
                        Property1 = "This is property 1",
                        Property2 = "This is property 2",
                        Property3 = "This is property 3",
                        Property4 = "This is property 4"
                    };

foreach (var current in b.Expressions)
{
    var compiled = current.Compile();
    var result = compiled.Invoke(instance);
    Console.WriteLine(result);
}

您的输出将是

  

这是属性1

     

这是属性2

     

这是属性3

     

这是属性4

答案 1 :(得分:0)

WithProperty是一种通用方法,它采用属性成员访问表达式的结果类型所隐含的类型参数,这就是TValue在其声明中表示的含义。由于您要使用反射来生成lambda,因此还必须动态进行WithProperty调用,以便您可以使用正确类型的调用(例如,WithProperty<String>用于String属性)。

这里是一个扩展方法,该方法生成一个lambda,该lambda由对类中所有属性的所有链接的WithProperty调用组成,然后编译并调用IDataExtractor上的lambda。我将所有调用链接在一起,然后进行编译,因为可能会有一些编译开销,所以我不想单独编译和调用每个属性的代码。

public static class IDataExtractorExt {
    public static IDataExtractor<TRow> WithAllProperties<TRow>(this IDataExtractor<TRow> extractor) {
        var p = Expression.Parameter(typeof(IDataExtractor<TRow>), "p"); // final lambda parameter
        Expression ansBody = p; // start with p => p

        var withPropGenericMI = typeof(IDataExtractor<TRow>).GetMethod("WithProperty"); // lookup WithProperty<> generic method
        var properties = typeof(TRow).GetProperties();
        foreach (var property in properties) {
            var withPropMI = withPropGenericMI.MakeGenericMethod(property.PropertyType); // instantiate generic WithProperty<> to property type
            var pxp = Expression.Parameter(typeof(TRow), "x"); // property accessor lambda parameter
            var pxb = Expression.PropertyOrField(pxp, property.Name); // property accessor expression x.property
            Expression propExpr = Expression.Lambda(pxb, pxp); // x => x.property
            ansBody = Expression.Call(ansBody, withPropMI, propExpr); // build up p => p.WithProperty(x => x.property)...
        }

        return ((IDataExtractor<TRow>)(Expression.Lambda(ansBody, p).Compile().DynamicInvoke(extractor)));
    }
}