4

我正在尝试制作自己的扩展方法,该方法可以采用任意数量的 lambda 表达式,但是每当我添加多个表达式时,它似乎都会窒息。

这是方法:

public static MvcHtmlString _RouteButton<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, string label, string controller, string action, params Expression<Func<TModel, TProperty>>[] parameters)
{
   var test = parameters;
   return MvcHtmlString.Empty;
}

这是成功调用它的标记:

<%: Html._RouteButton("details", "Health", "SystemDetails", m=>m.Id)%>

这是错误的标记:

<%: Html._RouteButton("details", "Health", "SystemDetails", m=>m.Id, m=>m.Status)%>

这是错误:

无法从用法中推断出方法的类型参数。尝试明确指定类型参数

任何帮助表示赞赏。谢谢!

4

4 回答 4

8

让我们简化一下:

using System;
class P
{
    static void M<R>(params Func<R>[] p) {}
    static void N(int i, string s, decimal m)
    {
        M(()=>i, ()=>s); // fails
        M(()=>i, ()=>s.Length); // succeeds
        M(()=>i, ()=>m); // succeeds
    }
}

现在很清楚为什么您的程序失败了?

在我的程序中,每个调用都尝试推断 R。在第一次调用中,R 被推断为 int 和 string,因此推断失败,因为没有类型既是 int 又是 string。在第二个中,R 被推断为 int 并且......再次为 int !成功是因为 int 匹配所有边界。第三种,R被推断为int和decimal,成功了,因为每一个int都可以隐式转换为decimal,所以decimal是一个很好的推断。

Id 和 Status 可能是类型不兼容的属性。如果你想这样做,那么推断的类型之一必须是最好的类型

请注意,C# 从不说“哦,我看到你推断出狗、猫和鱼的界限,所以我猜你的意思是动物”。C# 宁愿说“Dog、Cat 或 Fish 都不是最好的界限,所以我不知道你的意思;请明确说明”。

于 2013-02-14T17:43:21.887 回答
4

Eric Lippert 的回答解释了问题所在。我将添加如何实际解决它:您很可能不需要 lambda 来返回属性的类型,您只想获取表达式树来检查属性。因此,您可以做的是将类型从更改Func<TModel, TProperty>Func<TModel, object>(并删除TProperty类型参数)。由于 C# 中的所有普通类型都可以隐式转换为object,因此您的代码将在此更改后正常编译和运行

于 2013-02-14T21:55:00.323 回答
1

我想在网站上得到这个,以防有人尝试类似的事情。

该代码构建了一个路由表单。为此,它需要属性的名称及其

public static void _RouteButton<TModel>(this HtmlHelper<TModel> htmlHelper, string text, string controller, string action, params Expression<Func<TModel, object>>[] parameters)
{
    using (htmlHelper.BeginRouteForm("Default", new { controller = controller, action = action }))
    {
        foreach (Expression<Func<TModel, object>> p in parameters)
        {
            MemberExpression me;
            switch (p.Body.NodeType)
            {
                case ExpressionType.Convert:
                case ExpressionType.ConvertChecked:
                    var ue = p.Body as UnaryExpression;
                    me = ((ue != null) ? ue.Operand : null) as MemberExpression;
                    break;
                default:
                    me = p.Body as MemberExpression;
                    break;
            }
            string name = me.Member.Name;
            string value = p.Compile()(htmlHelper.ViewData.Model).ToString();
            HttpContext.Current.Response.Write(htmlHelper.Hidden(name, value).ToHtmlString());
        }
        HttpContext.Current.Response.Write("<input type='submit' value='" + text + "' />");
    }
}
于 2013-02-15T14:46:20.567 回答
-1

@eric-lippert 以您的示例解释了“为什么它不起作用”。每个表达式中传递的属性类型不一样。

将扩展方法的签名更改为仅指定 TModel,因为泛型将有助于解决问题。此外,为表达式函数指定object 代替TProperty。

扩展方法

public static MvcHtmlString _RouteButton<TModel>(this HtmlHelper<TModel> htmlHelper, 
                                                 params Expression<Func<TModel, object>>[] parameters)
{
    var test = parameters;
    return MvcHtmlString.Empty;
}

ASP/剃须刀

<%: Html._RouteButton(m=>m.Id, m=>m.Status)%>
@Html._RouteButton(m=>m.Id, m=>m.Status)
于 2016-11-24T04:50:54.820 回答