如何重新调整通用C#表达式的用途,允许输入类型参数发生更改?

如何重新调整通用C#表达式的用途,允许输入类型参数发生更改?,c#,asp.net-mvc,generics,lambda,C#,Asp.net Mvc,Generics,Lambda,编辑:简化问题现实世界的版本可以在下面找到,但也许没有必要回答这个问题 我有一个MethodCallExpression,我想重新调整它的用途。表达式调用主体引用的方法是正确的方法,但是,其中一个类型参数已更改。给定一个现有的MethodCallExpression,我想构造一个新的MethodCallExpression,它具有相同的主体表达式,但具有不同的类型参数。这可能吗?如何做到这一点?谢谢 现实世界的问题如下(除非您需要更具体的示例,否则请忽略此问题): 我有一个表达式expressi

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

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

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

我有一个表达式
expression
,用作显示模板(将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));
    }
    // ...
}
公共静态类HtmlHelperExtensions
{
公共静态MvcHtmlString FieldsetDisplayFor(此HtmlHelper HtmlHelper,表达式)
{
返回RenderFieldsetDisplayFor(htmlHelper,表达式,(helper,ex)=>helper.DisplayFor(ex));
}
公共静态MvcHtmlString BONDFIELDSETDISPLAYFOR(此HtmlHelper HtmlHelper,表达式)
{
返回RenderFieldsetDisplayFor(htmlHelper,表达式,(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));
    }
内部静态MvcHtmlString RenderFieldsetDisplayFor(此HtmlHelper HtmlHelper,
表情表情,
表达式(代理)
{
if(expression.ReturnType.IsContainerObject())
{
返回DisplayChildFieldsetsFor(表达式、htmlHelper、displayDelegate);
}
var labelBuilder=新标记生成器(“div”);
labelBuilder.AddCssClass(“显示标签”);
labelBuilder.InnerHtml=htmlHelper.LabelFor(表达式).ToHtmlString();
var inputBuilder=新标记生成器(“div”);
inputBuilder.AddCssClass(“显示字段”);
//此处调用表达式委托以呈现值的显示
inputBuilder.InnerHtml=displayDelegate.Compile().Invoke(htmlHelper,expression).ToHtmlString();
返回新的MvcHtmlString(String.Format(“{0}{1}”,labelBuilder,inputBuilder));
}
在expression.ReturnType.IsContainerObject()=true之前,此方法可以正常工作。DisplayChildFieldSetFor方法将从表达式的ReturnType获取所有适当的属性,并递归调用RenderFieldsetDisplayFor方法。我要做的是将父类型的属性馈送到displayDelegate表达式中。问题在于,每个属性的TValue类型都会改变,因为它最初是基于父类型构造的


那么,表情大师,你有什么建议吗?我是否需要创建一个访问者来更改lambda参数?或者,表达式参数是问题所在。我对Expression API非常陌生,我认为我对泛型的理解还不足以知道我的错误所在。

您可以尝试调用
DisplayChildFieldsetsFor
的特殊实例-无需深入研究表达式,但需要一些运行时反射(不确定是否需要缓存生成的方法)我不确定我们是否意见一致。
DisplayChildFieldsetsFor
的目的是从类型中获取适当的属性,并将每个属性传递到
RenderFieldsetDisplayFor
方法中。我考虑过将MethodInfo对象传递到
DisplayChildFiledsetsFor
,但我不知道这是否正确。本质上,我有我想在lambda表达式中调用的方法,但它不能按原样使用。我想我可能需要使用displayDelegate.Body和适当的参数类型来构建一个新表达式。我想你的问题是当你需要
DisplayChildFieldsetsFor
时调用
DisplayChildFieldsetsFor
,如果不是-忽略注释。是的,我想我可能已经把你弄糊涂了
DisplayChildFieldsetsFor
是表达式委托的来源。我需要采用该表达式的lambda形式,并使用相同的表达式体构建一个新表达式,但参数适用于当前子属性。这有意义吗?谢谢,@AlexeiLevenkov.Yes(我不知道怎么做)。我最好在问题的开头提供非常简化的示例(除了您真正的问题之外),并明确指出“忽略下面的文本,除非您真的想知道我为什么需要它”,并在希望更改表达式类型的代码中添加内联注释。