【问题标题】:Avoid or embrace C# constructs which break edit-and-continue?避免或接受破坏编辑并继续的 C# 构造?
【发布时间】:2011-04-21 09:30:23
【问题描述】:

我开发并维护了一个用 C# 2.0 编写的大型 (500k+ LOC) WinForms 应用程序。它是多用户的,目前部署在大约 15 台机器上。该系统的开发正在进行中(可以被认为是一个永久的测试版),并且几乎没有采取任何措施来保护用户免受每周构建中可能引入的潜在新错误的影响。

出于这个原因,除其他外,我发现自己变得非常依赖调试器中的编辑并继续。它不仅有助于寻找错误和修复错误,而且在某些情况下也有助于正在进行的开发。我发现能够从正在运行的应用程序的上下文中执行新编写的代码非常有价值——无需重新编译并向新代码添加特定的入口点(必须添加虚拟菜单选项、按钮等)应用程序并记住在下一次生产构建之前删除它们)- 一切都可以在不停止进程的情况下实时试用和测试。

我非常重视编辑并继续,因此我积极编写代码以完全兼容它。例如,我避免:

  • 匿名方法和内联委托(除非完全不可能重写)
  • 通用方法(稳定、不变的实用程序代码除外)
  • 以“任何 CPU”定位项目(即从不以 64 位执行)
  • 在声明点初始化字段(初始化移至构造函数)
  • 编写使用yield 的枚举器块(实用程序代码除外)

现在,我完全意识到 C# 3 和 4 中的新语言功能在很大程度上与编辑并继续(lambda 表达式、LINQ 等)不兼容。这是我拒绝将项目升级到框架的更新版本的原因之一。

我的问题是避免使用这些更高级的结构来支持非常非常容易调试的代码是否是一种好习惯?这种发展有合法性,还是浪费?此外,重要的是,这些构造(lambda 表达式、匿名方法等)是否会产生性能/内存开销,而编写良好、编辑和继续兼容的代码可以避免这些开销? ...或者 C# 编译器的内部工作是否使这些高级构造比手动编写的“扩展”代码运行得更快?

【问题讨论】:

  • 投票结束一个写得很好的问题是犯罪行为,但这里有一个similar question,可能会给你一些见解。

标签: c# debugging lambda anonymous-methods edit-and-continue


【解决方案1】:

不想听起来陈词滥调 - 编写单元/集成测试而不是依赖 Edit-Continue 是一种很好的做法。

这样,您只需花费一次精力,每隔一次就“免费”...

现在我不建议您为所有代码回顾性地编写单元;相反,每次您必须修复错误时,首先编写一个测试(或更常见的多个测试)来证明修复。

正如@Dave Swersky 在 cmets 中提到的那样,Mchael Feathers 的书中,Working Effectively with Legacy Code 是一个很好的资源(它是在你写完它 5 分钟后留下的,对吧?)

所以是的,我认为避免新的 C# 结构以支持编辑和继续是错误的;但我也认为仅仅为了新结构而拥抱新结构是错误的,尤其是当它们导致更难理解的代码时。

【讨论】:

  • 如果可以的话+100。在不使用单元测试的情况下专门编写代码来支持编辑和继续,说得委婉一点,恕我直言,这不是最好的方法。我还可以推荐一本书有效地使用遗留代码amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/…
  • 我要补充一点,我们在项目后期看到的大多数错误都与 GUI 和跨单元/集成相关,而不是发生在单个业务对象的范围内。这在很大程度上是一个维持和增强的阶段。如果可以对 GUI 组件进行单元测试,相信我,我会的!
  • +1:编辑并继续对于单元测试来说是一个完整的非问题
【解决方案2】:

我喜欢“编辑并继续”。我发现它是交互式开发/调试的巨大推动力,当它不起作用时我也觉得它很烦人。

如果“编辑并继续”有助于您的开发方法,那么一定要做出选择以促进它,记住您放弃的东西的价值。

我最讨厌的一个问题是,使用 lambda 表达式编辑函数中的任何内容都会破坏“编辑并继续”。如果我绊倒它,我可能会写出 lambda 表达式。我对 lambda 表达式持怀疑态度。我可以用它们更快地做一些事情,但如果我稍后把它们写出来,它们并不能节省我的时间。

就我而言,当我真的不需要时,我会避免使用 lambda 表达式。如果它们妨碍我,我可以将它们包装在一个函数中,以便我可以“编辑并继续”使用它们的代码。如果他们是无偿的,我可以写出来。

您的方法不必非黑即白。

【讨论】:

  • 为什么要删除我认为“编辑并继续”最有价值的上下文 - GUI 和算法设计。我认为这个编辑被冲昏了头脑。
【解决方案3】:

想稍微澄清一下这些事情

最好避免使用这些更高级的结构来支持非常非常容易调试的代码?

Edit and Continue 并不是真正的调试,它正在开发。我做出这种区分是因为新的 C# 特性非常易于调试。该语言的每个版本都添加了对新语言功能的调试支持,以使它们尽可能易于调试。

一切都可以在不停止过程的情况下实时试用和测试。

这种说法具有误导性。可以使用“编辑并继续”来验证更改是否修复了非常具体的问题。验证更改是否正确并且不会破坏许多其他问题要困难得多。即因为编辑和继续不会修改磁盘上的二进制文件,因此不允许进行单元测试等项目。

总的来说,虽然是的,但我认为避免新的 C# 结构以支持编辑和继续是错误的。编辑并继续是一个很棒的功能(当我在 C++ 时代第一次遇到它时真的很喜欢它)。但它作为生产服务器助手的价值并不能弥补新 C# 功能恕我直言的生产力收益。

【讨论】:

    【解决方案4】:

    我的问题是避免使用这些更高级的结构来支持非常非常容易调试的代码是否是一种好习惯

    我认为,任何时候你都在强迫自己编写代码:

    1. 表达较少
    2. 更长
    3. 重复(避免使用泛型方法)
    4. 不可移植(从不调试和测试 64 位??!?!?)

    您增加的总体维护成本远远超过调试器中“编辑并继续”功能的损失。

    我会尽可能写出最好的代码,而不是让你的 IDE 功能发挥作用的代码。

    【讨论】:

    • “最好”的意思我希望最容易理解!
    【解决方案5】:

    虽然您的方法本质上没有任何问题,但它确实将您限制在 IDE 所理解的表达量上。您的代码反映了 的功能,而不是语言的功能,因此您在开发世界中的整体价值会降低,因为您阻碍自己学习其他提高生产力的技术。避免使用 LINQ 以支持 Edit-and-Continue 对我个人而言感觉像是巨大的机会成本,但矛盾的是,您必须先获得一些经验,然后才能有这种感觉。

    此外,正如其他答案中提到的,对代码进行单元测试可以消除 始终运行整个应用程序的需求,从而以不同的方式解决您的困境。如果您不能在 IDE 中右键单击并仅测试您关心的 3 行代码,那么您已经在开发过程中做了太多的工作。

    【讨论】:

    • 我承认我的学术背景有点过时这些日子。对于这个项目来说已经太晚了——利益相关者没有意识到它的必要性,而且我们离结束太近了,它无法产生真正的影响。下次的好建议!
    【解决方案6】:

    您确实应该引入持续集成,它可以帮助您在部署软件之前发现并消除错误。尤其是大型项目(我认为 500k 相当大)需要某种形式的验证。

    http://www.codinghorror.com/blog/2006/02/revisiting-edit-and-continue.html

    关于具体问题:不要避免这些结构,也不要依赖您疯狂的调试技能 - 尽量避免出现错误(在已部署的软件中)。改为编写单元测试。

    【讨论】:

      【解决方案7】:

      我还参与过非常大型的永久测试版项目。

      我使用了匿名方法和内联委托来将一些相对简单的 use-one 逻辑保留在它们唯一的使用位置附近。

      为了重用和可靠性,我使用了泛型方法和类。

      我已尽可能完整地初始化构造函数中的类,以维护类不变量并消除由处于无效状态的对象引起的错误的可能性。

      我使用枚举器块将创建枚举器类所需的代码量减少到几行。

      所有这些都有助于将快速变化的大型项目保持在可靠状态。

      如果我无法编辑并继续,我会编辑并重新开始。这在大多数情况下会花费我几秒钟,在令人讨厌的情况下会花费几分钟。通过重用更好地推理代码的能力和更高的可靠性为我节省了几个小时,这是值得的。

      我会做很多事情来让查找错误变得更容易,但如果这样也更容易发现错误的话,我会做很多事情。

      【讨论】:

        【解决方案8】:

        你可以试试Test Driven Development。我发现完全避免使用调试器非常有用。你从一个新的测试开始(例如单元测试),然后你只运行这个单元测试来检查你的开发——你不需要整个应用程序一直运行。这意味着您不需要编辑并继续!

        我知道 TDD 是当前的流行词,但它确实对我有用。如果我需要使用调试器,我将其视为个人故障:)

        【讨论】:

          【解决方案9】:

          依靠编辑和继续。听起来好像很少有时间花在设计新功能上,更不用说单元测试了。我觉得这很糟糕,因为您最终可能会进行大量调试和错误修复,有时您的错误修复会导致更多错误,对吧?

          但是,很难判断你是否应该使用语言特性,因为这还取决于许多其他因素:项目要求、发布期限、团队技能、重构后的代码可管理性成本、举几个例子。

          希望这会有所帮助!

          【讨论】:

            【解决方案10】:

            您似乎遇到的问题是:

            重建您的应用程序需要很长时间, 重新启动它并开始工作 您正在处理的 UI。

            正如大家所说,单元测试将有助于减少您必须运行应用以查找/修复非 UI 代码中的错误的次数;但是它们对 UI 布局等问题没有帮助。

            过去我编写了一个测试应用程序,它会快速加载我正在处理的 UI 并用虚拟数据填充它,以减少循环时间。

            将任何 UI 代码分离到可以通过单元测试进行测试的其他类中,将允许您在这些类中使用所有 C# 构造。然后,您可以将 UI 代码中使用的构造限制为自身。

            当我开始编写大量单元测试时,我对“编辑并继续”的使用下降了,除了 UI 代码我几乎不使用它。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2021-07-07
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2021-02-09
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多