Lambda语句的Roslyn Only编译器错误:表达式无法转换为表达式树

时间:2015-08-05 14:55:18

标签: vb.net .net-4.5 moq visual-studio-2015 roslyn

问题

如何重写下面的Moq测试,以便它们在Roslyn中再次运行?显然我可以移除Lambda,但我希望避免这种情况。

另外,为了感兴趣:Roslyn编译器修复了一个允许这些无效语句以前工作的错误,或者这是一个现在打破这些语句的编译器错误?

详情

我试图将我的大部分VB.NET解决方案从Visual Studio 2013迁移到Visual Studio 2015.解决方案中的所有项目都针对.NET 4.5。我目前正在使用Moq 4.0进行这些测试。我有几个基于Moq的Lambda单元测试无法在Visual Studio 2015中编译,但在Visual Studio 2013中编译并运行良好。这些测试也适用于Visual Studio 2010和Visual Studio 2012。

大多数测试非常简单,看起来像这样:

Private _view As Mock(Of Views.ICreateSecurityUserView)

<Test>
Public Sub ValidateSave_CallWithBlankLogin_SetsViewToolError()
    _view = New Mock(Of Views.ICreateSecurityUserView)()

    _view.SetupGet(Of String)(Function(x) x.Login).Returns("")
    _view.SetupGet(Of String)(Function(x) x.LoginName).Returns(loginNameValue)

    _subject.ValidateSave()
    _view.Verify(Sub(x) x.LoginFieldError = It.Is(Of String)(Function(s) Not String.IsNullOrEmpty(s)), Times.Once)
End Sub

违规行将是这一行:_view.Verify(Sub(x) x.LoginFieldError = It.Is(Of String)(Function(s) Not String.IsNullOrEmpty(s)), Times.Once)

我得到的构建错误(如标题中所示)是:

  

错误BC36534表达式无法转换为表达式树。

我已经对表达式进行了一些调整,看看编译器是否会更加快乐,如果它是多行的:

_view.Verify(Sub(x)
                 x.LoginFieldError = It.Is(Of String)(Function(s)
                                                          Return Not String.IsNullOrEmpty(s)
                                                      End Function)
             End Sub, Times.Once)

但无济于事,因为这只会增加这些额外的错误(Visual Studio 2013编译器也不像多行版本):

  

错误BC36675语句lambda无法转换为表达式树。

错误&#34;解决方案&#34;

如果我将测试行更改为:

,我可以进行此编译
_view.Verify(Sub(x) VerifyFunctionNameError(x), Times.Once)

然后调用新的,无意义的函数:

    Private Sub VerifyFunctionNameError(x As Views.ICreateSecurityFunctionView)
        x.FunctionNameError = It.Is(Of String)(Function(s) Not String.IsNullOrEmpty(s))
    End Sub

2 个答案:

答案 0 :(得分:3)

你的问题很难回答,我不能在不知道声明的情况下得到一个体面的责备。然而,肯定有一个暗示为什么不应该从VB.NET语言规范编译。在您的计算机上找到它的副本C:\ Program Files(x86)\ Microsoft Visual Studio 14.0 \ VB \ Specifications \ 1033 \ Visual Basic语言规范.docx

导航到第11.1.1章并向下滚动一下以到达Annotation块。我将引用最相关的部分:

  

lambda方法和表达式树之间的确切转换可能无法在编译器版本之间修复,超出了本规范的范围。对于Microsoft Visual Basic 11.0,所有lambda表达式都可以转换为表达式树,但受以下限制:

  1.只有没有ByRef参数的单行lambda表达式才能转换为表达式树。在单行Sub lambdas中,只有调用语句可以转换为表达式树。

第一个值得注意的细节是微软不想确定准确的规则并且规范已经过时了。为什么他们不想指甲是相当明显的,限制是非常严重的,他们本来想留下改进的空间。

它确实明确说明为什么编译器版本都不能转换你的多行lambda方法,只支持单行。下一部分解释了为什么你不能在VS2015上运行它,你使用的是赋值语句而不是方法调用。

所以这强烈暗示VS2013有错,它不应该接受你的lambda方法。这是非常激烈的Roslyn解析器重写将加强语法规则并避免复制先前版本中的错误。还有足够的空间担心VS2013代码实际上无法正常工作。错误地将lambda解析为表达式而不是语句的非零赔率。换句话说,获取比较表达式而不是赋值语句。 VB.NET确实为这种歧义打开了大门,=运算符在表达式和赋值语句中都使用。这是一种猜测,如果没有重复,我无法验证。

最好从实际编写编译器的人那里反弹。您可以在connect.microsoft.com上提交反馈报告。一定要包括他们可以编译的最小repro,否则他们会很快放弃。请注意,他们很有可能将其关闭为“#34;设计&#34;。这就是它的样子。

答案 1 :(得分:-1)

我的上述&#34;糟糕的解决方案&#34;在我的问题中,无论如何甚至都没有工作。当测试运行时,这会失败,因为它显然无法从子测试中确定任何内容。

我的问题的实际答案是使用Mock.VerifySet,而不是Mock.Verify。我们实际上是在大多数地方这样做,所以我不确定为什么我们在这里使用其他方法。将在2013年 2015中运行的重写测试将如下所示:

_view.VerifySet(Sub(x) x.LoginFieldError = It.Is(Of String)(Function(s) Not String.IsNullOrEmpty(s)), Times.Once)

这个编译并且测试也正在通过。

我今天在Moq源代码中徘徊,下面是不同结果的原因:

验证

public void Verify(Expression<Action<T>> expression, Times times)
{
    Mock.Verify(this, expression, times, null);
}

<强> VerifySet

public void VerifySet(Action<T> setterExpression, Times times)
{
    Mock.VerifySet(this, setterExpression, times, null);
}

Expression<Action<T>>显然已更改其处理,而Action<T>似乎没有更改。