【问题标题】:How to create lambda expressions for every property of a class如何为类的每个属性创建 lambda 表达式
【发布时间】:2018-10-29 13:44:40
【问题描述】:

我有以下问题。我必须遍历类的所有属性来配置一些构建器。一个类有很多属性,所以代码比较繁琐。它看起来像这样:

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

代码在很多地方重复了很多不同的类型,不仅是 MyTypeWith1000Properties。我正在考虑创建一些扩展,如下所示:

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

然后在 WithAllProperties 中,我可以使用反射迭代类型属性,如下所示:

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 所期望的

【问题讨论】:

  • 看看MemberExpression 类。
  • 你似乎是从一个奇怪的角度来解决这个问题。你到底想做什么?我不明白为什么要制作那种可能以指数方式迭代的过滤器。
  • 我正在尝试简化代码 - 想象一下将这个构建器用于具有很多属性的不同类(为什么有这么多属性是另一回事)。您需要多次复制粘贴 WithProperty,然后将 lambda 更改为指向不同的属性。我不知道您所说的“过滤”是什么意思,以及为什么它会以指数方式迭代。
  • MyTypeWith100Properties 有什么特别之处,还是只是一个普通的类,有字段和属性?你只关心属性吗?

标签: c# lambda reflection expression


【解决方案1】:

更新 - 在 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

【讨论】:

  • 哦,它看起来不错,并且可以编译,但是...如果仅在以下行中 var lambda = Expression.Lambda>(property, parameter);该对象是一个实际的属性类型,因为现在我得到了运行时错误。
  • 你能分享一下你对 WithProperty 方法的实现吗?
  • 我更新了答案,将参数类型设置为表达式参数的类型。
  • 你能分享你的WithProperty方法的实现吗?抱歉 - 这是一个外部库,我不知道。你到底得到了什么错误?我得到的错误是:System.ArgumentException:'System.Object' 类型的表达式不能用于分配给'System.String' 类型。执行 extractor = extractor.WithProperty(lambda) 时崩溃
  • 所以我认为,问题出在这一行: var lambda = Expression.Lambda>(property, parameter);而不是对象,应该有一种对应的属性。我不知道如何让它变得动态 - 我不知道这是否可能。
【解决方案2】:

WithProperty 是一个泛型方法,它采用属性成员访问表达式的结果类型所隐含的类型参数,这就是TValue 在其声明中表示的内容。由于您想使用反射来生成 lambda,因此您还必须动态调用 WithProperty,以便您可以调用具有正确类型的那个(例如,WithProperty&lt;String&gt; 用于 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)));
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-10-06
    • 1970-01-01
    • 2011-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多