Good tooling, modern language features (C# 15 has the long awaited unions, closed hierarchies), great debugging experience, etc.
Good tooling, modern language features (C# 15 has the long awaited unions, closed hierarchies), great debugging experience, etc.
I'd like to hear more about your issue with the JIT and where do you find it a hindrance?
I've had a few benchmarks lately that performed better under JIT than AOT compilation, and while that isn't always the case, the JIT, especially on dotnet 10, performs very well.
Put another way, "100% pure .NET" was never a goal in the same way it was in the Java world, but I never saw this as holding it back any more than the reliance on native code holds back languages like Python, because both have very good integration stories.
My only complaint is the lack of cross-platform support for C++/CLI, as it was a very useful tool for integrating with libraries that lack a non-C++ API.
The performance in theory can be slightly better in some niches but my point is simply that it's a huge chunk of complexity for not much gain.
Yes, because when you think about it, why should they?
MS has showed many times how unreliable and even user-hostile they can be, so the community at large is very wary of their ecosystem in spite of real advancements.
I wouldn't qualify that with "even". Numerical stuff like that is one of Python's most well-developed niches, and Python's numerical ecosystem is the benchmark everything else needs to be compared against.
C# sweet spot is really web APIs (and all variants thereof), IMO.
Once you've tried a better type system (Rust), it's difficult to go back to C#. People jump through all kinds of hoops to get good performance out of it, and they're mostly able to.
Things I dearly miss from Rust when I'm using C#:
- Associated types in interfaces. (IEnumerable should not be forced to return a type-erased = heap allocated IEnumerator instance.)
- Move-only structs. (Currently, every type with value semantics is trivially copyable, even ref structs.)
Foreach loops use compile-time duck typing, so if you don't intend to use your enumerable in LINQ then both the enumerable and enumerator don't need to implement any interfaces.
This optimisation covers only one common use case, but that's the way of things in C# land.
slightly off topic but a lot of things in .NET actually use compile time duct typing. like how async works To make your own task, you don't add an interface to it. You just implement a single method.
Meanwhile, iterators in Rust frequently outperform manual loops.
> Zero allocation LINQ with LINQ to Span, LINQ to SIMD, and LINQ to Tree (FileSystem, JSON, GameObject, etc.) for all .NET platforms and Unity, Godot.Its not but if you call from the interface and not the implementation, it has to box because that's the contract. If you for each over a List<T> and not IEnumerable you get a stack allocated value type IEnumerator.
Like, I get that it’s “fine” for a lot of things, but it’s very not fine in situations where you really need to guarantee that a particular operation doesn’t, say, trigger GC, like in a video game.
Associated types: https://github.com/dotnet/csharplang/issues/8712
Ownership: https://github.com/agocke/csharplang/blob/ownership/proposal...
If you are doing greenfield, you can also make compilation much stricter with Roslyn analyzers and neutralize some of the bullet points here:
https://yannickloth.github.io/independentvariation/software/...
Sure, it's a valid language design, but it's also a negative in "understandability of each line of code", and thus maintainability, and even stuff like tooling, verification.
C# is often like Java but without taking a wrong turn when it came to type-erased generics, the generics are uncomplicated enough even if slightly less powerful than what you get with the full instantiation of C++.
C++ was severely lacking before reflection came along in 26, I'm actually writing some new reflection based libraries and it's mostly a joy and quite capable.
But C++ has so many outrightly dangerous semantics when it comes to writing secure code, the coping of pointing to realtime systems,etc as another security "dimension" doesn't hold when Rust,etc exist today.
C# is good in that it's "easy" yet the performance profile of the features allows optimized code to run far closer to C++/rust/etc in performance compared to Java,etc. (that said, a wast majority is probably running like Java due to programmer patterns, but not needing to drop down to another language when it's needed feels good).
If you want to do insane meta programming, you should look into Roslyn generators. It's like they added a robust code gen system to the language (even though its a compile stack feature). You basically get the full power of templates and macros but you get to use the target language (C#) instead of a bolted on macro or template language.
More generally, the fact that the C# compiler is implemented as library with easy support for runtime code generation is also a powerful feature that I've found to be incredibly useful in a variety of expected and unexpected ways.
You still get interop with C# and .NET but the syntax and language is superior IMO. If only Ms would invest some resources into it.
Rust would be too expensive Engineering wise even with AI today.
So yes "async" is easy, but "multithreaded/parallel" is a lot more error prone and explicit.
EDIT: I should also note I havent been in the space for a few years so Im not fully current on how it looks today
Multi-threaded/parallel is a LOT easier now with the Task Parallel Library (TPL, https://learn.microsoft.com/en-us/dotnet/standard/parallel-p...) and System.Threading.Channels. Channels in particular are nice because it can fan out and then back in to one aggregator, removing the need for self managing synchronization primitives.
I usually default to channels first, TPL when I want threads.
Greenthreads or what have you are not a replacement if you want to do things like have a UI thread or bind to a native thread for interop. But you _can_ with async/await.
ConcurrentML really brought dotnet's concurrency story into the 1990s.