避免或接受会破坏编辑并继续的 C# 结构?

发布于 2024-09-26 08:25:37 字数 833 浏览 5 评论 0原文

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

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

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

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

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

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

I develop and maintain a large (500k+ LOC) WinForms app written in C# 2.0. It's multi-user and is currently deployed on about 15 machines. The development of the system is ongoing (can be thought of as a perpetual beta), and there's very little done to shield users from potential new bugs that might be introduced in a weekly build.

For this reason, among others, i've found myself becoming very reliant on edit-and-continue in the debugger. It helps not only with bug-hunting and bug-fixing, but in some cases with ongoing development as well. I find it extremely valuable to be able to execute newly-written code from within the context of a running application - there's no need to recompile and add a specific entry point to the new code (having to add dummy menu options, buttons, etc to the app and remembering to remove them before the next production build) - everything can be tried and tested in real-time without stopping the process.

I hold edit-and-continue in such high regard that I actively write code to be fully-compatible with it. For example, I avoid:

  • Anonymous methods and inline delegates (unless completely impossible to rewrite)
  • Generic methods (except in stable, unchanging utility code)
  • Targeting projects at 'Any CPU' (i.e. never executing in 64-bit)
  • Initializing fields at the point of declaration (initialisation is moved to the constructor)
  • Writing enumerator blocks that use yield (except in utility code)

Now, i'm fully aware that the new language features in C# 3 and 4 are largely incompatible with edit-and-continue (lambda expressions, LINQ, etc). This is one of the reasons why i've resisted moving the project up to a newer version of the Framework.

My question is whether it is good practice to avoid using these more advanced constructs in favor of code that is very, very easy to debug? Is there legitimacy in this sort of development, or is it wasteful? Also, importantly, do any of these constructs (lambda expressions, anonymous methods, etc) incur performance/memory overheads that well-written, edit-and-continue-compatible code could avoid? ...or do the inner workings of the C# compiler make such advanced constructs run faster than manually-written, 'expanded' code?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

别忘他 2024-10-03 08:25:37

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

这样,您只需花费一次精力,其他时间都是“免费”的……

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

正如 @Dave Swersky 在评论中提到的,Mchael Feathers 的书,有效地使用遗留代码 是一个很好的资源(在你写完它 5 分钟后它就成为遗产了,对吧?)

所以是的,我认为避免新的 C# 结构而支持编辑并继续是一个错误;但我也认为仅仅为了新的结构而接受新的结构是错误的,特别是如果它们导致代码更难理解的话。

Without wanting to sound trite - it is good practice to write unit/integration tests rather than rely on Edit-Continue.

That way, you expend the effort once, and every other time is 'free'...

Now I'm not suggesting you retrospectively write units for all your code; rather, each time you have to fix a bug, start by writing a test (or more commonly multiple tests) that proves the fix.

As @Dave Swersky mentions in the comments, Mchael Feathers' book, Working Effectively with Legacy Code is a good resource (It's legacy 5 minutes after you wrote it, right?)

So Yes, I think it's a mistake to avoid new C# contructs in favor of allowing for edit and continue; BUT I also think it's a mistake to embrace new constructs just for the sake of it, and especially if they lead to harder to understand code.

多情出卖 2024-10-03 08:25:37

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

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

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

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

你的方法不需要非黑即白。

I love 'Edit and Continue'. I find it is a huge enabler for interactive development/debugging and I too find it quite annoying when it doesn't work.

If 'Edit and Continue' aids your development methodology then by all means make choices to facilitate it, keeping in mind the value of what you are giving up.

One of my pet peeves is that editing anything in a function with lambda expressions breaks 'Edit and Continue'. If I trip over it enough I may write out the lambda expression. I'm on the fence with lambda expressions. I can do some things quicker with them but they don't save me time if I end up writing them out later.

In my case, I avoid using lambda expressions when I don't really need to. If they get in the way I may wrap them in a function so that I can 'Edit and Continue' the code that uses them. If they are gratuitous I may write them out.

Your approach doesn't need to be black and white.

淡忘如思 2024-10-03 08:25:37

想要澄清一下这些事情

避免使用这些更高级的构造而使用非常非常容易调试的代码是一种很好的做法吗?

编辑并继续并不是真正的调试,而是开发。我做出这样的区分是因为新的 C# 功能非常易于调试。该语言的每个版本都添加了对新语言功能的调试支持,以使它们尽可能轻松地进行调试。

一切都可以实时尝试和测试,而无需停止过程。

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

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

Wanted to clarify these things a bit

it is good practice to avoid using these more advanced constructs in favor of code that is very, very easy to debug?

Edit and Continue is not really debugging, it is developing. I make this distinction because the new C# features are very debuggable. Each version of the language adds debugging support for new language features to make them as easy as possible debug.

everything can be tried and tested in real-time without stopping the process.

This statement is misleading. It's possible with Edit and Continue to verify a change fixes a very specific issue. It's much harder to verify that the change is correct and doesn't break a host of other issues. Namely because edit and continue doesn't modify the binaries on disk and hence doesn't allow for items such as unit testing.

Overall though yes I think it's a mistake to avoid new C# contructs in favor of allowing for edit and continue. Edit and Continue is a great feature (really loved it when I first encountered it in my C++ days). But it's value as a production server helper doesn't make up for the producitivy gains from the new C# features IMHO.

你是年少的欢喜 2024-10-03 08:25:37

我的问题是,避免使用这些更高级的构造而使用非常非常容易调试的代码是否是一种好的做法

我认为任何时候你强迫自己编写这样的代码:

  1. 表达力较低
  2. 、重复时间较长
  3. (避免通用方法)
  4. 不可移植(从不调试和测试 64 位?!?!?)

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

我会编写尽可能最好的代码,而不是使 IDE 的某个功能正常工作的代码。

My question is whether it is good practice to avoid using these more advanced constructs in favor of code that is very, very easy to debug

I would argue that any time you are forcing yourself to write code that is:

  1. Less expressive
  2. Longer
  3. Repeated (from avoiding generic methods)
  4. Non-portable (never debug and test 64bit??!?!?)

You are adding to your overall maintenance cost far more than the loss of the "Edit and Continue" functionality in the debugger.

I would write the best code possible, not code that makes a feature of your IDE work.

影子是时光的心 2024-10-03 08:25:37

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

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

While there is nothing inherently wrong with your approach, it does limit you to the amount of expressiveness understood by the IDE. Your code becomes a reflection of its capabilities, not the language's, and thus your overall value in the development world decreases because you are holding yourself back from learning other productivity-enhancing techniques. Avoiding LINQ in favor of Edit-and-Continue feels like an enormous opportunity cost to me personally, but the paradox is that you have to gain some experience with it before you can feel that way.

Also, as has been mentioned in other answers, unit-testing your code removes the need to run the entire application all the time, and thus solves your dilemma in a different way. If you can't right-click in your IDE and test just the 3 lines of code you care about, you're doing too much work during development already.

空城缀染半城烟沙 2024-10-03 08:25:37

您确实应该引入持续集成,这可以帮助您在部署软件之前找到并消除错误。特别是大型项目(我认为 500k 相当大)需要某种验证。

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

关于具体问题:不要避免这些构造,也不要依赖你疯狂的调试技巧 - 尽量避免错误(在已部署的软件中)。而是编写单元测试。

You should really introduce continues integration, which can help you to find and eliminate bugs before deploying software. Especially big projects (I consider 500k quite big) need some sort of validation.

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

Regarding the specific question: Don't avoid these constructs and don't rely on your mad debugging skills - try to avoid bugs at all (in deployed software). Write unit tests instead.

夏花。依旧 2024-10-03 08:25:37

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

我使用匿名方法和内联委托来将一些相对简单的一用逻辑部分保留在其唯一使用位置附近。

我使用通用方法和类来实现重用和可靠性。

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

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

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

如果我无法编辑并继续,我会编辑并重新开始。大多数情况下,这会花费我几秒钟的时间,在严重的情况下会花费几分钟。更强大的代码推理能力和通过重用获得的更高可靠性拯救了我,这几个小时是值得的。

我会做很多事情来让 bug 更容易被发现,但如果这也能让 bug 更容易出现的话,我会做很多事情。

I've also worked on very large permanent-beta projects.

I've used anonymous methods and inline delegates to keep some relatively simple bits of use-one logic close to their sole place of use.

I've used generic methods and classes for reuse and reliability.

I've initialised classes in constructors to as full an extent as possible, to maintain class invariants and eliminate the possibility of bugs caused by objects in invalid states.

I've used enumerator blocks to reduce the amount of code needed to create an enumerator class to a few lines.

All of these are useful in maintaining a large rapidly changing project in a reliable state.

If I can't edit-and-continue, I edit and start again. This costs me a few seconds most of the time, a couple of minutes in nasty cases. Its worth it for the hours that greater ability to reason about code and greater reliability through reuse saves me.

There's a lot I'll do to make it easier to find bugs, but not if it'll make it easier to have bugs too.

夜深人未静 2024-10-03 08:25:37

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

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

You could try Test Driven Development. I found it very useful to avoid using the debugger at all. You start from a new test (e.g. unit test), and then you only run this unit test to check your development - you don't need the whole application running all the time. And this means you don't need edit-and-continue!

I know that TDD is the current buzz-word, but it really works for me. If I need to use the debugger I take it as a personal failure :)

谁对谁错谁最难过 2024-10-03 08:25:37

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

然而,很难判断是否应该使用语言功能,因为这还取决于许多其他因素:项目要求、发布截止日期、团队技能、重构后代码可管理性的成本等等。 。

希望这有帮助!

Relying on Edit and Cont. sounds as if there is very little time spent on designing new features, let alone unit tests. This I find to be bad because you probably end up doing a lot of debugging and bug fixing, and sometimes your bug fixes cause more bugs, right?

However, it's very hard to judge whether you should or should not use language features or not, because this also depends on many, many other factors : project reqs, release deadlines, team skills, cost of code manageability after refactoring, to name a few.

Hope this helps!

寄居者 2024-10-03 08:25:37

您似乎遇到的问题是:

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

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

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

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

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

The issue you seem to be having is:

It takes too long to rebuild you app,
start it up again and get to the bit
of UI you are working on.

As everyone has said, Unit Tests will help reduce the number of times you have to run your app to find/fix bugs on none UI code; however they don’t help with issues like the layout of the UI.

In the past I have written a test app that will quickly load the UI I am working on and fill it with dummy data, so as to reduce the cycle time.

Separating out none UI code into other classes that can be tested with unit tests, will allow you do use all C# constructs in those classes. Then you can just limit the constructs in use in the UI code its self.

When I started writing lots of unit tests, my usage of “edit-and-continue” went down, I how hardly use it apart from UI code.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文