代码之家  ›  专栏  ›  技术社区  ›  Ecyrb

将func<t>重构为expression<func<t>

  •  5
  • Ecyrb  · 技术社区  · 15 年前

    我现在有一个方法 Func<Product, string> 作为参数,但我需要它是 Expression<Func<Product, string>> . 使用AdventureWorks,下面是一个使用func的示例。

    private static void DoSomethingWithFunc(Func<Product, string> myFunc)
    {
        using (AdventureWorksDataContext db = new AdventureWorksDataContext())
        {
            var result = db.Products.GroupBy(product => new
            {
                SubCategoryName = myFunc(product),
                ProductNumber = product.ProductNumber
            });
        }
    }
    

    我希望它看起来像这样:

    private static void DoSomethingWithExpression(Expression<Func<Product, string>> myExpression)
    {
        using (AdventureWorksDataContext db = new AdventureWorksDataContext())
        {
            var result = db.Products.GroupBy(product => new
                {
                    SubCategoryName = myExpression(product),
                    ProductNumber = product.ProductNumber
                });
        }
    }
    

    但是,我遇到的问题是 myExpression(product) 无效(无法编译)。在阅读了其他一些帖子之后,我明白了原因。如果不是因为我需要 product 我的键的第二部分的变量我可能会这样说:

    var result = db.Products.GroupBy(myExpression);
    

    但我确实需要 产品 变量,因为我确实需要键的第二部分(productNumber)。所以我不知道现在该怎么办。我不能把它当作一个函数,因为那样会引起问题。我不知道如何使用表达式,因为我不知道如何通过 产品 变量。有什么想法吗?

    编辑: 下面是一个如何调用该方法的示例:

    DoSomethingWithFunc(product => product.ProductSubcategory.Name);
    
    2 回复  |  直到 15 年前
        1
  •  4
  •   Pavel Minaev    15 年前

    无法拼接表示为 Expression<T> 对象放到由lambda表达式表示的“树文本”的中间。必须构造表达式树才能传递给 GroupBy 手动:

    // Need an explicitly named type to reference in typeof()
    private class ResultType
    {
         public string SubcategoryName { get; set; }
         public int ProductNumber { get; set; }|
    }
    
    private static void DoSomethingWithExpression(
        Expression<Func<Product,
        string>> myExpression)
    {
        var productParam = Expression.Parameter(typeof(Product), "product");
        var groupExpr = (Expression<Func<Product, ResultType>>)Expression.Lambda(
            Expression.MemberInit(
               Expression.New(typeof(ResultType)),
               Expression.Bind(
                   typeof(ResultType).GetProperty("SubcategoryName"),
                   Expression.Invoke(myExpression, productParam)),
               Expression.Bind(
                   typeof(ResultType).GetProperty("ProductNumber"),
                   Expression.Property(productParam, "ProductNumber"))),
            productParam);
        using (AdventureWorksDataContext db = new AdventureWorksDataContext())
        {
            var result = db.Products.GroupBy(groupExpr);
        }
    }
    
        2
  •  3
  •   dahlbyk    15 年前

    再想一想,编译表达式是行不通的。

    您需要手工构建GroupBy表达式,这意味着您不能使用匿名类型。我建议构建表达式的其余部分,然后反编译以查看生成的表达式树。最终结果是这样的,使用 myExpression 酌情:

    private static void DoSomethingWithExpression(Expression<Func<Product, string>> myExpression)
    {
        var productParam = myExpression.Parameters[0];
    
        ConstructorInfo constructor = ...; // Get c'tor for return type
    
        var keySelector = Expression.Lambda(
                              Expression.New(constructor,
                                  new Expression[] {
                                      productParam.Body,
                                      ... // Expressions to init other members
                                  },
                                  new MethodInfo[] { ... }), // Setters for your members
                              new [] { productParam });
    
        using (AdventureWorksDataContext db = new AdventureWorksDataContext())
        {
            var result = db.Products.GroupBy(keySelector);
    
            // ...
        }
    }