如何重用通用C#表达式,允许输入类型参数更改?

时间:2013-01-16 16:44:53

标签: c# asp.net-mvc generics lambda

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

我有一个MethodCallExpression,我想重新调整一下。表达式调用的主体引用的方法是正确的方法,但是,其中一个类型参数将发生更改。给定一个现有的MethodCallExpression,我想构造一个新的MethodCallExpression,它具有相同的body表达式但是不同的类型参数。这可能吗?如何实现这一目标?感谢。

真实世界的问题(除非你需要更具体的例子,否则忽略这一点):

我有一个表达式Expression<Func<HtmlHelper<TModel>, Expression<Func<TModel, TValue>>, MvcHtmlString>>,用作显示模板(将HtmlHelper和Expression&gt;作为输入并输出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的类型将根据父类型最初构建的每个属性进行更改。

那么,表达大师,你推荐什么?我是否需要创建一个访问者来更改lambda参数?或者,表达式参数是问题。我是Expression API的新手,我认为我不了解泛型,不知道我哪里出错了。

0 个答案:

没有答案