7 ms·
>How is it possible that I can write efficient and provably-safe code in C# without a degree in type theory? Because Anders Hejlsberg is one of the greatest la
by DeathArrow 2y ago
>How is it possible that I can write efficient and provably-safe code in C# without a degree in type theory?
Because Anders Hejlsberg is one of the greatest language architects and the C# team are continuing that tradition.
The only grudge I have against them is they promised us discriminated unions since forever and they are still discussing how to implement it. I think that is the greatest feature C# is missing.
For the rest C# is mostly perfect. It has a good blend of functional and OOP, you can do both low level and high level code. You can target both the VM or the bare hardware. You can write all types of code beside system programming (due to the garbage collector). But you can do web backend, web front-end, services, desktop, mobile apps, microcontroller stuff, games and all else. It has very good libraries and frameworks for whatever you need. The experience with Visual Studio is stellar.
And the community is great. And for most domains there is generally only one library or framework everybody uses so you not only don't have to ask what to use for a new feature or project, but you also find very good examples and help if you need.
It feels like a better, more strait trough version of Java, less verbose and less boiler plate-y. So that's why .NET didn't need its own Kotlin.
Sure, it can't meet the speed of Rust or C# for some tasks because of the garbage collector. But provided you AOT compule, disable the garbage collector and do manual memory management, it should.
- jabwd 2y agoExcept that the GC makes it exactly not viable for games and its one of the biggest problems Unity devs run into. I agree it's a great language, but its not a do it all.
- lmm 2y agoMany of the top games in recent years have used it, so you've got a funny definition of "not viable".
- johnisgood 2y agoOr roll their own, so they used GC in one way or another.
- neonsunset 2y agoUnity (and its GC) is not representative of the performance you get with CoreCLR. The article discusses ref lifetime analysis that does have relationship with GC, but it does not force you into using one. Byrefs are very special - they can hold references to stack, to GC-owned memory and to unmanaged memory. You can get a pointer to device mapped memory and wrap it with a Span<T> and it will "just work".
- mafuy 2y agoWell, when I worked in Unity I used to compile C# code with the LLVM backend. It was as fast as C++ code would be. So Unity is perhaps an example in favor of C#.
- SeasonalEnnui 2y agoI could argue the opposite - GC makes it more viable for games. "GC is bad" misses too much nuance. It goes like this: developer very quickly and productively gets minimum viable game going using naive C# code. Management and investors are happy with speed of progress. Developers see frame rate stutters, they learn about hot path profiling, gen0/1/2/3 GC & how to keep GC extremely fast, stackalloc, array pooling, Span<T>, native alloc; progressively enhancing quickly until there are no problems. These advanced concepts are quick and low risk to use, and in the case of many of the advanced concepts; what you would be doing in other languages anyway.
- DeathArrow 2y agoThe only reason we might see FPS drop in games, is not because C# and its GC. It's mostly because the poor usage of the graphics pipeline and the lack of optimization. As a former game developer I had to do a lot of optimization so our games run nicely on mobile phones with modest hardware. C# it's plenty fast for game programming.
- SeasonalEnnui 2y agoA good datapoint, thanks. Extending my original point - C# got really good in the last 5 years with regards to performance & low-level features. There might be an entrenched opinion problem to overcome here.
- mycocola 2y agoThat entirely depends on the game. Recent example is Risk of Rain 2, which had frequent hitches caused by the C# garbage collector. Someone made a mod to fix this by delaying the garbage collection until the next load-screen — in other words, controlled memory leakage. The developers of Risk of Rain 2 were undoubtedly aware of the hitches, but it interfered with their vision of the game, and affected users were left with a degraded experience. It's worth mentioning that when game developers scope of the features of their game, available tech informs the feature-set. Faster languages thus enable a wider feature-set.
- 2y ago
- greener_grass 2y ago> not viable for games > Unity devs run into So it's viable but not perfect
- DeathArrow 2y ago> one of the biggest problems Unity devs run into Unity used Mono. Which wasn't the best C# implementation, performance wise. After Mono changed its license, instead of paying for the license, Unity chose to implement their infamous IL2CPP, which wasn't better. Now they want to use CoreCLR which is miles better than both Mono and IL2CPP.
- Paradigma11 2y agoDoesnt Unity use its own GC or transpiles to C++? Unity on .Net core is more than a year away, no?
- pjmlp 2y agoIt uses the prehistoric Mono GC. Additionally it transpiles IL to C++ due to many targets like consoles, and iDevices, not allowing for a JIT. They also have a C# subset called Burst, which could have been avoided if they were using .NET Core.
- bob1029 2y agoGC in modern .NET runtime is quite fast. You can get very low latency collections in the normal workstation GC mode. Also, if you invoke GC intentionally at convenient timing boundaries (I.e., after each frame), you may observe that the maximum delay is more controllable. Letting the runtime pick when to do GC is what usually burns people. Don't let the garbage pile up across 1000 frames. Take it out every chance you get.
- Traubenfuchs 2y ago> if you invoke GC intentionally at convenient timing boundaries (I.e., after each frame), Manually invoking GC many times per second is a viable approach?
- bob1029 2y agoIt has worked well in my prototypes. There is a reason a GC.Collect method is exposed for use.
- smaudet 2y agoAt least for this instance you have a good idea which objects are "ripe" for collection. There should be some way to specify "collect these, my infra objects don't need to be".
- munificent 2y agoIt can be, yes. You're basically trading off worse throughput for better latency. If you forcibly run the GC every frame, it's going to burn cycles repeatedly analyzing the same still-alive objects over and over again. So the overall performance will suffer. But it means that you don't have a big pile of garbage accumulating across many frames that will eventually cause a large pause when the GC runs and has to visit all of it. For interactive software like games, it is often the right idea to sacrifice maximum overall efficiency for more predictable stable latency.
- neonsunset 2y ago
- pjmlp 2y agoExcept that is a matter of developer skill, and Unity using Mono with its lame GC implementation, as proven by CAPCOM's custom .NET Core fork based engine used for Devil May Cry on the PlayStation 5.
- rafaelmn 2y agoC# has much better primitives for controlling memory layout than Java (structs, reified generics). BUT it's definitely not a language designed for no-gc so there are footguns everywhere - that's why Rider ships special static analysis tools that will warn you about this. So you can keep GC out of your critical paths, but it won't be pretty at that point. But better than Java :D
- neonsunset 2y ago> but it won't be pretty at that point Possibly prettier than C and C++ still. Every time I write something and think "this could use C" and then I use C and then I remember why I was using C# for low-level implementation in the first place. It's not as sophisticated and good of a choice as Rust, but it also offers "simpler" experience, and in my highly biased opinion pointers-based code with struct abstractions in C# are easier to reason about and compose than more rudimentary C way of doing it, and less error-prone and difficult to work with than C++. And building final product takes way less time because the tooling is so much friendlier.
- zigzag312 2y agoGames would need alternative GC optimized for low latency instead of maximum throughput. AFAIK it has been possible to replace the GC with alternative implementation for the past few years, but no one has made one yet. EDIT: Some experimental alternative GC implementations: https://github.com/kkokosa/UpsilonGC https://github.com/kkokosa/UpsilonGC https://www.codeproject.com/Articles/5372791/Implementing-a-Simple-Garbage-Collector-in-Csharp https://www.codeproject.com/Articles/5372791/Implementing-a-...
- lifthrasiir 2y agoAt least for Unity, the actual problem lies in IL2CPP and not C#. I have professionally used C# in real-time game servers and GC was never a big issue. (We did use C++ in the lower layer but only for the availability of Boost.Asio, database connectors and scripting engines.)
- Rohansi 2y agoUnity lets you use either IL2cPP (AOT) or Mono (JIT). Either way it will use Boehm GC which is a lot worse than the .NET GC. If your game servers weren't using Unity then they are using a better GC.
- lifthrasiir 2y agoYeah, we rolled our own server framework in .NET mainly because we were doing MMOs and there were no off-the-shelf frameworks (including Unity's) explicitly designed for that. In fact, I believe this is still mostly true today.
- alkonaut 2y agoIt's hard to use C# without creating garbage. But it's not impossible. Usually you'd just create some arenas for your important stuff, and avoid allocating a lot of transient objects such as enumerators etc. So long as you can generate 0 bytes of allocation each frame, you won't need a GC no matter how many frames you render. The question is only this: does it become so convoluted that you could just as well have used C++?
- jayd16 2y agoEnumerators are usually value types as long as you use the concrete type. Using the interface will box it. You can work around this by simply using List<T> as the type instead of the IEnnumerable. You have to jump through some hoops but it's really not that convoluted and miles easier than good C++.
- alkonaut 2y agoThe problem with it is that you don't know. The fundamental language construct "foreach" is one that may or may not allocate and it's hard for you as a developer to be sure. Many other low level things do this or at least used to (events/boxing/params arrays, ...). I wish there was an attribute in C# that was "[MustNotAllocate]" which files the compilation on known allocations such as these. It's otherwise very easy to accidentally introduce some tiny allocation into a hot loop, and it only manifests as a tiny pause after 20 minutes of runtime.
- neonsunset 2y agoMost often you do know whether an API allocates. It is always possible to microbenchmark it with [MemoryDiagnoser] or profile it with VS or Rider. I absolutely love Rider's dynamic program analysis that just runs alongside me running an application with F5, ideally in release, and then I can go through every single allocation site and decide what to do. Even when allocations happen, .NET is much more tolerant to allocation traffic than, for example, Go. You can absolutely live with a few allocations here and there. If all you have are small transient allocations - it means that live object count will be very low, and all such allocations will die in Gen 0. In scenarios like these, it is uncommon to see infrequent sub-500us GC pauses. Last but not least, .NET is continuously being improved - pretty much all standard library methods already allocate only what's necessary (which can mean nothing at all), and with each release everything that has room for optimization gets optimized further. .NET 9 comes with object stack allocation / escape analysis enabled by default, and .NET 10 will improve this further. Even without this, LINQ for example is well-behaved and can be used far more liberally than in the past. It might sound surprising to many here but among all GC-based platforms, .NET gives you the most tools to manage the memory and control allocations. There is a learning curve to this, but you will find yourself fighting them much more rarely in performance-critical code than in alternatives.
- Rohansi 2y agoUnity has literally the worst implementation of C# out there right now. Not only is it running Mono instead of .NET (Core) but it's also not even using Mono's generational GC (SGen). They have been working on switching from Mono to .NET for years now because Mono isn't being updated to support newer C# versions but it will also be a significant performance boost, according to one of the Unity developers in this area [1]. IL2CPP, Unity's C# to C++ compiler, does not help for any of this. It just allows Unity to support platforms where JIT is not allowed or possible. The GC is the same if using Mono or IL2CPP. The performance of code is also roughly identical to Mono on average, which may be surprising, but if you inspect the generated code you'll see why [2]. [1] https://xoofx.github.io/blog/2018/04/06/porting-unity-to-coreclr/#performance-with-coreclr https://xoofx.github.io/blog/2018/04/06/porting-unity-to-cor... [2] https://www.jacksondunstan.com/articles/4702 https://www.jacksondunstan.com/articles/4702 (many good articles about IL2CPP on this site)
- para_parolu 2y agoI believe Unity switched to net core last year
- Rohansi 2y agoThey did not - it is still a work in progress with no announced target release date. They also have no current plans to upgrade the GC being used by IL2CPP (their C# AOT compiler). https://discussions.unity.com/t/coreclr-and-net-modernization-unite-2024/1519272 https://discussions.unity.com/t/coreclr-and-net-modernizatio...
- jayd16 2y agoThe two biggest engines, Unreal and Unity, use a GC. Unity itself uses C#. C# is viable for games but you do need to be aware of the garbage you make. It's really not that hard to structure a game that pre-allocates and keeps per frame allocs at zero.
- _7acn 2y agoWhat do you think about F#, then? It already covers everything you mentioned and it has discriminated unions.
- rafaelmn 2y agoBasically C# is like using a Mac and F# is "I use Arch btw."
- johnisgood 2y agoJust to comment on the meme: it used to be Gentoo instead of Arch in my good old days. :P
- _7acn 2y agoHowever amusing, this comparison doesn’t seem accurate to me. F# may appear more challenging only to someone who is already accustomed to OO programming languages. For people just starting to code, without pre-existing habits, learning F# could be much easier than learning C#
- roetlich 2y agoBut knowing to effectively programming F# requires you to understand OOP and the functional abstractions. Most libraries in .Net still target C#, so understanding C# syntax and mentally translating it to F# is often required. If your application doesn't require many outside dependencies that might be different, but for most projects F# will require a lot more learning.
- miloandmilk 2y agoThis 1000 times. I have been learning F# for a while now, and while the functional side that is pushed heavily is a joy to use, anything that touches the 'outside world' is going to have way more resources for C# as far as libraries, official documentation, general information including tutorials etc. You will need to understand and work with those. So you really do need to understand C# syntax and semantics. Additionally there are a few concepts that seem the same in each language but have different implementations and are not compatible (async vs tasks, records) so there is additional stuff to know about when mentally translating between C# and F#. I really want to love F# but keep banging my head against the wall. Elixir while not being typed yet and not being as general purpose at least allows me to be productive with it's outstanding documentation, abundance of tutorials and books on both the core language and domain specific applications. It is also very easy to mentally translate erlang to elixir and vice versa in the very few occasions needed.
- zigzag312 2y ago> The only grudge I have against them is they promised us discriminated unions since forever and they are still discussing how to implement it. I think that is the greatest feature C# is missing. To ease the wait you could try Dunet (discriminated union source generator). https://github.com/domn1995/dunet https://github.com/domn1995/dunet
- CharlieDigital 2y agoThere's also OneOf (https://github.com/mcintyre321/OneOf https://github.com/mcintyre321/OneOf) as well which feels more ergonomic to me (having used both). Practical example in a short write up here: https://chrlschn.dev/blog/2024/07/csharp-discriminated-unions-and-dotnet-channels/ https://chrlschn.dev/blog/2024/07/csharp-discriminated-union...
- zigzag312 2y agoIsn't OneOf more like a type union, and not a tagged/discriminated union?
- deleted 2y ago[deleted]
- whizzter 2y agoI'm hardly missing discriminated unions (sure exhaustive checking would be nice) anymore since the introduction of switch expressions that in combination with records handles most practical cases.
- CharlieDigital 2y agoI find DU's particularly useful with `System.Threading.Channels` because it lets the channel handle multiple types of results in one stream.
- GTP 2y agoHow does C# fares in terms of portability these days? I checked years ago, and at the time, for non-Windows OSes you had to use Mono. But whether your application was going to work or not also depended on which graphic libraries you were using, e.g. WinForms wasn't going to work on Mono. At the time, C# was presented to me as a better Java, but to me it seemed that Java had true cross-platform compatibility while C# was going to work nicely only on Windows, unless you had some proper planning of checking beforehand which libraries were working with Mono.
- Rizu 2y agoyou should checkout AvalonuiaUI[0] or unoPlatform[1] if wanting to target web/mobile/window/linux/macOS if building for the web online, asp.net core runs on Linux servers as well as windows and there's MAUI [2] ( not a fan of this), you are better-off with with the others. in summary c# and .NET is cross-platform, third party developers build better frameworks and tools for other platform while Microsoft prefers to develop for Microsoft ecosystem, if you get [0] https://avaloniaui.net/ https://avaloniaui.net/ [1] https://platform.uno/ https://platform.uno/ [2] https://learn.microsoft.com/en-us/dotnet/maui/what-is-maui?view=net-maui-8.0 https://learn.microsoft.com/en-us/dotnet/maui/what-is-maui?v...
- smaudet 2y ago> and there's MAUI [2] ( not a fan of this), you are better-off with with the others. I will say MS has been obsessed with trying to take a slice of the mobile pie. However their Xamarin/WPF stuff left so much to be desired and was such a Jenga Tower that I totally get the community direction to go with a framework you have ostensibly have more control over vs learning that certain WPF elements are causes of e.g. memory leaks...
- CharlieDigital 2y agoVery good. I work at one of the few startups that uses C# and .NET. Dev machines are all M1/M3 MacBook Pros and we deploy to a mix of x64 and Arm64 instances on GCP and AWS. I use VS Code on macOS while the rest of the team prefers Rider. Zero friction for backend work and certainly more pleasant than Node. (We still use Node and JS for all front-end work).
- factormeta 2y ago>The experience with Visual Studio is stellar. I assume you mean just the Windows Visual Studio? The Mac version is not exactly on par with the Windows. Yeah C# is great, but one would need Window's version of VS (NOT VS Code) to take full advantage of C#. For me that is a deal breaker, when the DX of a language is tight to a proprietary sourced IDE by MS.
- moomin 2y agoThese days, JetBrains have stepped into the gap with Rider. Rider isn't perfect, but there's definitely people who prefer it to Visual Studio.
- dachris 2y agoRider definitely can hold a candle to Visual Studio. In my dev bubble there's about a 50/50 split for C# devs (mostly .NET Core) using VS vs Rider
- sfn42 2y agoIn mine the VS crew is the minority by far. VS is much worse, especially without ReSharper and at that point why not just use rider.
- moomin 2y agoI’m sure you can point to many things Rider is better at, but I’ve found enough sharp edges (including, annoyingly, it not being able to infer types that Roslyn can) that it’s not a sell for me. It’s also much faster and supports NCrunch.
- sfn42 2y agoNever heard of ncrunch but I googled it and its described as > the ultimate live testing tool for Microsoft Visual Studio and JetBrains Rider So it seems at least that part of your critique is outdated. I'm not sure what you mean about the inference, I've never had any problem with that that I can remember. And it can be a bit slow to start up or analyze a project at first load but in return it gives much better code completion and such.
- alkonaut 2y agoThe DU stuff is enormous once you consider all the corners it touches. Especially with refinements. E.g. in code like if (s is string or s is int) { // what's the type of s here? is it "string | int" ? } And not to mention that the BCL should probably get new overloads using DU's for some APIs. But there is at least a work in progress now, after years of nothing.
- colejohnson66 2y agoOne of the claimed benefits of .NET Core was that they could improve the runtime at a much faster pace than .NET Framework did, especially if that meant adding new features or even IL opcodes. And they've done this before, with a big one (IMO) being ref fields in ref structs. Lately, when it comes to developing C#, the language design team has frustratingly been trying to shoehorn everything into the compiler instead of modifying the runtime. Then they say the runtime should be modified to pattern-match what they output. If DUs are to be implemented fully in C#, niches would probably be impossible. This means Optional<T>, when T is a class, would take two words.
- pif 2y ago> For the rest C# is mostly perfect. No, it isn't. The power of C++ templates is still astronomically far from C# generics.
- jayd16 2y agoWhat about the built in Roslyn source generator stuff? Is that sufficiently abusable? I'm not a templates/macro guy so I'm curious what's missing.
- DeathArrow 2y agoI don't miss that power. I remember having to modify some template heavy code and it brought me to the verge of madness. :)
- roetlich 2y agoYes, another plus point of C#. Sorry for the snark, but I do think C# compile are just barely acceptable for me, so I'm happy they aren't adding more heavy compile time features.
- lifthrasiir 2y agoThat power is usually considered too reckless to retain and simultaneously too cumbersome to actually use, partly because it was never planned in the first place.
- galangalalgol 2y agoC++ concepts restrain that recklessness, and people hate them for it. Rust will get most of that power when they finally stabilize const generic expressions. I like c# but like the article says, it isn't really borrow checking, so you don't get fearless concurrency. If I want to do coarse grained multi threading (not just a parallel for loop or iterator to speed up math) I only want to use rust now. Once I stopped having to think around thread safety issues and data consistency, I didn't want to go back. But for something single threaded c# or go are great and performant.
- 2y ago
- pif 2y ago> For the rest C# is mostly perfect. No! It misses "typedef", both at module API level and within generics.
- algorithmsRcool 2y ago> Because Anders Hejlsberg is one of the greatest language architects and the C# team are continuing that tradition. I wish Anders was still in charge of C# :(
- nick_ 2y agoFingers crossed that he'll one day come back and make T#
- deleted 2y ago[deleted]
- yndoendo 2y agoMy long term experience with Visual Studio is the inverse of stellar. In order to submit bugs with Microsoft the application redirects the end user to their website with web socket. The company I work for has extra security and this breaks preventing me from filing a cornucopia of bugs with Visual Studio. I cannot even file a bug on how the submit system is broken with Visual Studio. Closing and re-opening Visual Studio is a daily task. Most often during code refactoring of multiple parts. Creating new classes has inconsistency of template usage in the second most recent released version. Compile error message history can become stale and inconsistent where the output console does not. Pasting content into the resource manager is still broken during tab entry. Modal dialogs still cover the screen during debug. And those don't even touch the inconstant and buggy user experience. C# is a tool and like all tools it is good for some things and really bad for others. No tool is perfect. You can still use a ball-peen hammer for roofing but be better to have a roofing hammer. I would use Swift on iOS and Kotlin on Android for those platform projects, I don't even know those languages, and wouldn't use C#.