2

编辑:简化问题真实世界的版本可以在下面找到,但也许没有必要回答这个问题。

我有一个 MethodCallExpression,我想重新利用它。表达式调用的主体引用的方法是正确的方法,但是其中一个类型参数将发生变化。给定一个现有的 MethodCallExpression,我想构造一个具有相同主体表达式但类型参数不同的新 MethodCallExpression。这可能吗?如何做到这一点?谢谢。

真实世界的问题如下(除非您需要更具体的示例,否则请忽略此问题):

我有一个Expression<Func<HtmlHelper<TModel>, Expression<Func<TModel, TValue>>, MvcHtmlString>>用作显示模板的表达式(将 HtmlHelper 和 Expression> 作为输入并输出 McvHtmlString)。如果 TValue 是一个自定义类实例,那么它的属性必须输入到表达式委托而不是对象本身。这是有问题的,因为这意味着 TValue 将从父对象更改为当前属性的类型。(代码如下)

我不确定我应该如何解决这个问题。我知道可以使用 ExpressionVisitor 来实现表达式的这种更改,但我也在考虑我的表达式可能过于冗长的事实。请帮忙。

我试图创建的是用于呈现标签/值字段对的 HtmlHelper 扩展方法(ASP.NET MVC)。但是,我有绑定和未绑定字段的概念。绑定字段只是用一些剔除属性呈现。在我尝试使用 C# Expression API 重构一些可重用的组件之前,这种方法运行良好。

首先,这是用于将字段集呈现为绑定或未绑定元素的两种公共扩展方法。

public static class HtmlHelperExtensions
{
    public static MvcHtmlString FieldsetDisplayFor<TModel, TValue>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TValue>> expression)
    {
        return RenderFieldsetDisplayFor(htmlHelper, expression, (helper, ex) => helper.DisplayFor(ex));
    }

    public static MvcHtmlString BoundFieldsetDisplayFor<TModel, TValue>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TValue>> expression)
    {
        return RenderFieldsetDisplayFor(htmlHelper, expression, (helper, ex) => helper.BoundDisplayFor(ex));
    }
    // ...
}

请注意传递给 RenderFieldsetDisplayFor 方法的两个不同的表达式。这本质上是一个委托,较低级别的方法将使用它来呈现值,如下所示:

    internal static MvcHtmlString RenderFieldsetDisplayFor<TModel, TValue>(this HtmlHelper<TModel> htmlHelper,
                                                                    Expression<Func<TModel, TValue>> expression,
                                                                    Expression<Func<HtmlHelper<TModel>, Expression<Func<TModel, TValue>>, MvcHtmlString>> displayDelegate)
    {
        if (expression.ReturnType.IsContainerObject())
        {
            return DisplayChildFieldsetsFor(expression, htmlHelper, displayDelegate);
        }

        var labelBuilder = new TagBuilder("div");
        labelBuilder.AddCssClass("display-label");
        labelBuilder.InnerHtml = htmlHelper.LabelFor(expression).ToHtmlString();

        var inputBuilder = new TagBuilder("div");
        inputBuilder.AddCssClass("display-field");

        // expression delegate called here for the rendering display of the value
        inputBuilder.InnerHtml = displayDelegate.Compile().Invoke(htmlHelper, expression).ToHtmlString();

        return new MvcHtmlString(String.Format("{0}{1}", labelBuilder, inputBuilder));
    }

此方法工作正常,直到 expression.ReturnType.IsContainerObject() == true。DisplayChildFieldsetsFor 方法将从表达式的 ReturnType 中获取所有适当的属性,并递归调用 RenderFieldsetDisplayFor 方法。我想要做的是将父类型的属性提供给 displayDelegate 表达式。问题是每个属性的 TValue 类型都会改变,因为它最初是基于父类型构造的。

那么,Expression 大师们,您有什么推荐的吗?我需要创建一个访问者来更改 lambda 参数吗?或者,是表达式参数的问题。我对 Expression API 很陌生,我认为我对泛型的了解不足以知道我哪里出错了。

4

0 回答 0