C#编译器错误:无法转换lambda表达式

5
我试图使用Lambda表达式和反射来获取成员的层次结构名称(而不是使用文本常量),以在编译时检测控件绑定信息是否无效。
这是一个ASP.NET MVC项目,但据我所知它不是一个MVC特定的问题。编辑:具体来说,我希望以下内容能够评估为真:
string fullname = GetExpressionText(model => model.Locations.PreferredAreas);
"Locations.PreferredAreas" == fullname;

相反,我得到一个编译错误:

错误4:无法将lambda表达式转换为类型'System.Linq.Expressions.LambdaExpression',因为它不是委托类型。

为什么在下面的第二种情况下参数有效,但在第一种情况下无效?

// This doesn't compile:
string tb1 = System.Web.Mvc.ExpressionHelper.
    GetExpressionText(model => model.Locations.PreferredAreas);

// But this does:
MvcHtmlString tb2 =
    Html.TextBoxFor(model => model.Locations.PreferredAreas);

以下是 ASP.NET MVC Codeplex 项目相关代码。我认为它似乎将同一参数传递给同一方法:
// MVC extension method
public static MvcHtmlString TextBoxFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IDictionary<string, object> htmlAttributes) {
    ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
    return TextBoxHelper(
        htmlHelper,
        metadata,
        metadata.Model,
        ExpressionHelper.GetExpressionText(expression),
        htmlAttributes);
}

// MVC utility method
public static string GetExpressionText(LambdaExpression expression) {
    // Split apart the expression string for property/field accessors to create its name
    // etc...

你忘记包含问题中最相关的部分了——GetExpressionText源代码 :) - Oleg Tarasov
没关系,这是一个MVC助手。请看我的回答。 - Oleg Tarasov
根据Eric Lippert的回答(他的回答内容,而不仅仅是他能够回答的事实),GetExpressionText背后的源代码并不相关,对吗? - shannon
你还记得你最终做了什么吗?Eric提到的代表在哪里? - Luminous
请看下面的答案,Lum。 - shannon
3个回答

15

这个错误消息是正确的。一个lambda表达式可以转换成一个兼容的委托类型D或者兼容的委托类型的表达式Expression<D>,其中Expression<Func<TM, TP>>就是其中之一。 "LambdaExpression"不属于这些类型。因此,您会在尝试将lambda表达式转换为LambdaExpression时出现错误,但不会出现实际的表达式树类型的错误。 在其中必须有一个委托


谢谢Eric,我相信你是对的。我正在努力理解它 :) - shannon
1
所以你的意思是 (lambda to Expression<D> to LambaExpression) 是有效的并且是正在发生的,但是 (lambda to LambdaExpression) 不是吗? - shannon
@shannon:正确。LambdaExpression是Expression<D>的基类Lambda需要知道委托类型是什么。你只是告诉它“这个lambda表达式应该被视为一个lambda表达式”,这并没有给编译器任何提示。你需要说“这个lambda表达式应该被视为一个表达式树,用于接受一个int并返回一个字符串的表达式”或者其他什么。必须在其中包含一个委托类型。 - Eric Lippert
是的,这就是我困惑的地方,因为TM和TP都已经被推断出来了,所以编译器已经知道lambda是一个接受TM并返回TP的表达式。不过我相信我会解决它的 :) - shannon
@shannon:TM是从哪里推断出来的?(您发布的调用站点和扩展方法似乎不匹配。)当您解决它时,您会发现TM并不是从lambda中推断出来的;lambda中没有足够的信息来确定“model”的类型必须是什么;这些信息必须来自某个地方,在LambdaExpression情况下,您没有向编译器提供该信息。 - Eric Lippert
啊,我明白了,谢谢 - 我碰巧使用了Model作为变量,让它困扰了我:“model”在这里没有任何信息,也没有没有模型的TP。关于不匹配,我在尝试简洁时错过了一个重载,但它只添加了一个空字典。 - shannon

3

在尝试修复lambda表达式之前,请确保已添加以下引用:

System.Linq;
System.Linq.Expressions;

缺少这些引用可能会导致相同的错误("Cannot convert lambda expression to type 'System.Linq.Expressions.Lambda Expression' because it is not a delegate type")。


2

我认为你应该尝试使用类似这样的帮助方法:

public static string GetExpressionText<M, P>(this M model, Expression<Func<M, P>> ex)
{
    return GetExpressionText(ex);
}

看起来这可以工作,但 M 的实例并非必需,所以这实际上不属于扩展方法,对吧? - shannon
确切地说,您可以将此帮助程序声明为GetExpressionText<M,P>(Expression<Func<M,P>> ex),但在这种情况下,您必须明确指定类型参数。扩展方法语法更加简洁,尽管它需要声明一个“未使用”的参数。 - Oleg Tarasov
抱歉,我不能在两个地方分配答案 - Eric修复了我的大脑,你给了我所需的代码...这是一个抉择,但我认为Eric回答了我的问题并尝试(几乎成功地)教会一个人钓鱼。 - shannon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接