【问题标题】:How do I make a method that accepts multiple lambda expressions as parameters?如何创建一个接受多个 lambda 表达式作为参数的方法?
【发布时间】:2013-01-30 13:37:36
【问题描述】:

我正在尝试制作自己的扩展方法,该方法可以采用任意数量的 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)%>

这是错误:

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

感谢任何帮助。谢谢!

【问题讨论】:

  • Id和Status有哪些类型?
  • 您是否可以使用多个 Lambda 来获取多个参数,而不是只使用一个来返回具有必要字段的新匿名对象? &lt;%: Html._RouteButton("details", "Health", "SystemDetails", m=&gt; new { id= m.Id, status = m.Status})%&gt;
  • @ShaneA 你不能这样做,因为如果涉及匿名类型,你就不能为参数定义类型。
  • Shane,我实际上有一个覆盖,但为了简单起见,我想传递 lambda 表达式。

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


【解决方案1】:

让我们简化一下:

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 显然都不是最好的界限,所以我不知道你的意思;请明确说明”。

【讨论】:

  • 我可以看到失败的模式,但似乎类型是 Func 而不是字符串、整数或小数。为什么.Net会进行比方法签名更严格的验证?
  • @Nathaniel:R 不是类型。 R 是一个占位符,必须用类型填充。 (与参数不是值一样,参数是必须提供值的位置,称为参数。)编译器告诉您它无法确定您打算用什么类型替换 R .
  • @Nathaniel:这是编译时间限制,而不是运行时错误。它与编组无关。这样看。当您有一个方法 M&lt;T&gt; 并调用它时,您需要提供一个类型参数。您要求说出M&lt;int&gt;,就像您在制作新列表时要求说出List&lt;int&gt;一样。它必须是一个的列表。该方法必须是某物的方法。现在,出于礼貌,如果您省略&lt;int&gt;,编译器将尝试猜测您的意思。如果编译器无法猜测,那么它会告诉你。
  • 但是您必须说出方法类型参数是什么,或者提供编译器可以猜测的足够信息你想说什么。你什么都没有做,所以你得到一个编译时错误。
  • @Nathaniel:仍然是编译时错误,而不是运行时错误。
【解决方案2】:

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

【讨论】:

  • 这正是我最终得到的解决方案。我必须再等几个小时才能获准发布,但我会在那时发布。
【解决方案3】:

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

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

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 + "' />");
    }
}

【讨论】:

    【解决方案4】:

    @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/Razor

    <%: Html._RouteButton(m=>m.Id, m=>m.Status)%>
    @Html._RouteButton(m=>m.Id, m=>m.Status)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-07-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-28
      相关资源
      最近更新 更多