4 ms·
In Go, you have to explicitly do a defer. In C#, you just write `using var file = File.Open(...);`. Some methods handle disposal for you - you never interact wi
by neonsunset 2y ago
In Go, you have to explicitly do a defer. In C#, you just write `using var file = File.Open(...);`. Some methods handle disposal for you - you never interact with it if all you need is
foreach (var line in File.ReadLines("example.txt")) { ... }
Here, file handle will be opened once you start enumeration and will be closed through enumerator disposal which is one of the calls foreach is desugared into (to be accurate, this specific implementation will also close the handle when reading the last line, but I'd expect user-authored type to do handle closure through dispose implementation only)
I agree that Rust implicitly inserting drop() by proving the exact place the value gets dropped at is superior, but C# experience is not exactly terrible either.
- pjmlp 2y agoAdditionally, there are Roslyn analysers that will complain when we forget about using.
- CharlieDigital 2y agoTo be clear, `Dispose()` (aka `using`) is not the same as GC. It just cleans up unmanaged resources (connections, file handles, etc.) and marks the managed entity available for GC.
- pjmlp 2y agoDepends, C# and .NET in general have support for manual memory management, it is a matter how it was allocated in first place. People keep forgetting CLR was designed to accommodate C++ as well, and during the years some of those capabilities have been exposed to other languages in more easier ways than reaching out to C++/CLI.
- lionkor 2y agoOf course, I understand you don't leak resources if you pay attention and use linters. However, default out-of-scope behavior doesn't exist. In Go, you can defer, and you can also forget to defer. The default behavior is that resource cleanup is a program logic problem that is entirely on the programmer. The same applies to C#: You program your resource freeing with Dispose (okay, fair so far), and then if the caller forgets to Dispose, you leak. Again, the default behavior is that resource cleanup is a part of the explicitly implemented logic, always. Compare that to Rust, where you can explicitly drop(), but once a value goes out of scope, its resources are freed. This is about default and consistent behavior. C# and Go handle memory as a special resource you only sometimes, only in some ways, need to worry about (new, make) but most other resources (files, sockets, threads (if they exist)) become an afterthought and have special logic the programmer needs to worry about *by default*.