为什么在调试器中无法编辑包含匿名方法的方法?

21

每当我在一个方法中编写Lambda表达式或匿名方法时,如果我没有写对,我就被迫重新编译和重新启动整个应用程序或单元测试框架来修复它。这非常令人恼火,我最终浪费了比使用这些构造节省的时间还要多的时间。这太糟糕了,以至于我尽可能远离它们,尽管Linq和Lambda是我最喜欢的C#特性之一。

我认为有一个很好的技术原因会导致这种情况,或许有人知道吗?此外,有人知道这个问题是否会在VS2010中得到解决吗?

谢谢。

5个回答

21

为什么你不能编辑lambda函数?原因很简单,成本太高了。在C#(或VB)中启用此功能的成本非常高。

编辑lambda函数是难以通过当前ENC(Edit'n'Continue)架构解决的一类ENC问题特例。主要困难在于,对于以下情况中的任何一个,都很难修改或编辑ENC:

  1. 生成类形式的元数据
  2. 编辑或生成泛型方法

第一个问题更多的是逻辑约束,但它也遇到了ENC架构中的一些限制。问题在于生成第一个类并不是非常困难。麻烦在于在第二次编辑之后生成类。ENC引擎必须开始跟踪符号表,不仅跟踪活动代码,还要跟踪生成的类。通常情况下这不是大问题,但当基于其所使用的上下文来定义生成类的形状时(如由于闭包的原因而出现在lambda中),这变得越来越困难。更重要的是,如何解决与已经存在于进程中的类实例的区别?

第二个问题是CLR ENC架构的严格限制。C#(或VB)无法绕过此限制。

不幸的是,lambda函数正好同时遇到这两个问题。简而言之,ENC lambda涉及对现有类进行大量的修改(这些类可能已从其他ENC生成)。问题在于如何解决新代码与当前进程空间中现有闭包实例之间的差异。此外,lambda倾向于比其他代码更多地使用泛型,并且遇到了问题#2。

细节非常复杂,不适合在SO答案中详细解释。我考虑写一篇长篇博客文章。如果我能完成它,我将链接回这个特定的答案。


这是您需要证明 VS 使用社区想要的东西吗?我感觉有很多动力朝着函数式编程风格和泛型类型推断的方向发展,这两者都倾向于在方法中添加 lambda 表达式。对我来说,很少写没有 lambda 表达式的方法。 - marr75
2
@marr75,VS团队非常清楚客户希望在调试场景(ENC + watch、locals等)中更好地支持lambda表达式,该功能几乎每个版本都会被考虑。不幸的是,这是一个非常昂贵的功能。我写了两篇关于此主题的博客文章,试图分解该功能并解释其复杂性和高成本。http://blogs.msdn.com/b/jaredpar/archive/2010/06/02/why-is-linq-absent-from-debugger-windows-part-2.aspx - JaredPar
2
如果能够在不影响生成的类(相同字段、相同属性等)的情况下添加编辑和继续支持,那就至少很好了。这将在20%的工作量中解决至少80%的问题。我经常编辑带有lambda的函数,但不是lambda本身。有时我修改lambda,但它不会改变匿名类型。 - Ondrej Petrzilka
1
@Blackfighter VB.Net 已经支持此功能,但 C# 仍然缺乏。 - JaredPar
我是唯一一个需要一分钟才能理解ENC指的是什么吗?也许需要来点咖啡因... - Gene
最新版的VS中现在可以实现这个功能了吗? - Per G

1

有点遗憾的是,这个功能在VB中部分支持,但在C#中不支持: http://msdn.microsoft.com/en-us/library/bb385795.aspx

在C#中实现相同的行为将会减少80%的痛苦程度,特别是对于包含lambda表达式的函数,我们不需要修改lambda表达式或任何依赖它们的表达式,而且可能不需要付出“巨大代价”。


1
根据支持的代码更改列表,您不能向现有类型添加字段。匿名方法编译成奇怪命名的类(类似于<>_c__DisplayClass1),它们恰好就是类型。即使您对匿名方法的修改不包括更改封闭变量的集合(添加这些将更改现有类的字段),我想这就是无法修改匿名方法的原因。

2
你不需要修改匿名方法本身就能发现这个问题,只需修改包含匿名方法的方法即可,似乎甚至一个空格都足以引起问题... - Eyvind

0
重新启动单元测试应该只需要几秒钟的时间。 老实说,我从来不喜欢“编辑和继续”模型 - 在我的看法中,你应该总是重新运行整个程序以防止执行期间的更改影响之前运行的代码。 鉴于此,最好使用可以快速运行的单元测试。 如果您的单个单元测试需要很长时间才能开始,那么您应该考虑解决这个问题。
编辑:至于为什么它不起作用-您可能会发现它适用于某些lambda表达式,但不适用于其他lambda表达式。 不捕获任何变量(包括 this )的Lambda表达式被缓存到私有静态变量中,因此仅创建委托的一个实例。 更改代码意味着重新初始化该变量,这可能会产生有趣的副作用。

我知道你会知道答案的;-) - Kai Huppmann
在我看来,许多问题的答案是要避免一开始就陷入痛苦的境地。 - Jon Skeet
1
你说得没错,就单元测试框架而言,从概念上讲是正确的。然而,即使在VS中显示单元测试“停靠窗口”,当解决方案变得足够大时,也需要很长时间,这最终会耗费很多时间,不幸的是。 - Eyvind
2
ENC在UI应用中非常酷。如果你已经有了结构,你可以在调试会话中开发整个“应用程序”(当你有实时连接时很好)。 - leppie
我在此提名这篇回答为@JonSkeet的最不受欢迎的回答:在提名时只有1个赞(我知道你们会在这之后开始点赞,把它搞砸)。这就像在谷歌上搜索只得到1个结果(加上广告)...机会有多大!顺便说一下,这也是我们的政客们为什么继续对我们撒谎的原因,哈哈。 - Adi H
显示剩余2条评论

0

我只是想指出,Visual Studio 在这种情况下对“编辑”的考虑可能有点愚蠢(或者至少可以这么说)。当我在 git 中检查旧提交并尝试运行单元测试时,结果出现了 9 个错误(其中包括 ENC0014 和其他一些错误)。

因此,在没有修改任何文件的情况下,每次我尝试调试单元测试时都会出现这些错误。重新启动 Visual Studio 可以消除这些错误,因此我猜测根本问题在于缺少缓存失效,即 Visual Studio 无法检测/响应通过其编辑器窗口之外的文件更改。


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