Heh, I was afraid of being called out as a Microsoft shill. You're right, though—.NET really is that good.
I'll go ahead and say that open-sourcing the .NET ecosystem is the single best decision that Microsoft has taken in recent history.
any
use case (except perhaps embedded) with .NET.
How so? Do you mean it doesn't support ephemeral ftrace-based tracing or something? That seems unlikely to be true given core CLR features like reflection as well as Microsoft's extensive history of embedding .NET into services and devices.
This is just not the environment a memory managed language, running atop a virtual machine runtime is suitable for. You'd have to strip all that away, and then you'd just be left with C# syntax, but it would be very far from being C# and would not be compatible with any standard C# library, framework or platform.
For a lot of embedded work, even Rust is having a difficult time getting traction. Even the C++ Standard Library doesn't fit on many of the systems folks are working on, let alone the STL.
The system I'm working on at my day job has about 1meg of Flash and, IIRC, 128k RAM. It was reduced in resources to save cost (disposable device). The one I'm working on at home has about 4M Flash and 256k RAM. One of them is making liberal use of the STL and I know both have Python implementations available.
Tiny, memory constrained devices are still around, but they're a smaller and smaller part of the pie every year.
The performance ceiling of C# is extremely high now though, if you use the low level features. Unsafe blocks, even simd intrinsics! But at that point you aren't really doing C# so much, but at least you can dip into those features for performance critical parts of your code without having to pull out a new language and compiler and complicate your workflow and build tools.
I wouldn't really agree on this point.
Unsafe blocks have been a feature since C# 1.0 They may not be the default way of writing C#, but they've always been a natural and integral part of C# and have extensive use internally to ensure that interop can work and extra performance is available where possible.
SIMD acceleration has likewise been a part of the BCL (standard library) for nearly 10 years now and is just as integrated behind the scenes. It being part of the formal BCL makes it even more "standard" than the equivalent in C/C++ where such functionality is relegated to compiler specific headers/extensions.
SIMD code itself is also very idiomatic C#. There is nothing really different about utilizing the APIs, the only difference is in how you think about handling your data. Needing to think differently about how data is handled for some contexts is applicable to many domains in C# (and programming in general). It's no different than making code work with async or multi-threading ;)
Simply put, all these features are still C# and I don't think its necessarily good to say that using them means you're not really writing C# anymore. I view it as a disservice to the language, its extensibility, and power. It also leaves a connotation that you might be better off writing C/C++ instead, which is often not the case.
Now we take multi-core, 64-bits, gigabyte address spaces, memory protection and multi-tasking Unix-like OSs for granted most of the time. Except for the most stringent requirements (such as hard real time or very constrained power budgets) that we find small bare metal platforms.
And those are real fun to do ( but not as much fun to debug).
I specifically left embedded out because I didn't really want to make assumptions about CLR byte code running within the constraints of such environments. I've only ever programmed in C++ on embedded devices, so I can't actually comment on CLR (ergo the perhaps in my comment).
It is actually a pretty nice framework, but it does (did) require a 32bit CPU at minimum, and a hefty amount of flash.
That said, the UI framework was a joy to work with!
I should do some sort of write up of just .netMF, but I don't honestly recall too much about it in a general sense, since most of my time was spent in the weeds optimizing code.
I'm terrible at clickbait titles, so I sadly don't see much hope for future articles achieving any significant level of popularity!
If you really want to get a sense for how broad the APIs are today:
https://learn.microsoft.com/en-us/dotnet/api/system.numerics...
I've been working on a custom game engine in .NET for about a year now, and I still haven't had to pull down a 3rd party nuget.
It should provide up to 48x perf improvements as compared to .NET 7
I believe Vector4.Transform(Vector4, Matrix4x4) is my hottest path right now, but as I get into more complex scene graphs, Matrix4x4.Multiply(Matrix4x4, Matrix4x4) will likely pop into the profiler.
For `Matrix4x4.Multiply(Matrix4x4, Matrix4x4)` its a bit less at `10.04ns` to `8.38ns` (~17% faster)
The main improvements tracked by: * https://github.com/dotnet/perf-autofiling-issues/issues/1144... * https://github.com/dotnet/perf-autofiling-issues/issues/1148... * https://github.com/dotnet/perf-autofiling-issues/issues/1146...
There are some more cases to be improved. `Matrix4x4.Multiply` in particular I need to update to use a proper SIMD implementation rather than its current scalar implementation.
If you have any other core scenarios where it's important, let me know and I can try to focus on them sooner, rather than later ;)
I have no special requests at this time. I'll dig into the above issues and comment on GH as needed.
Also, there's a ton of world class third party tooling around .NET that really makes everything better. Shout-out to LINQPad for saving me literally 100s of hours on testing, queries and quick POCs.
In the modern ASP.NET Core, things are much better organized and documented. The framework has far less magic involved. No magically auto-generated repositories, no @Autowired magic, and far less use of annotations. Things just make more sense.
As for the language itself, C# is nicer in many regards. Even basic things like properties instead of hand-written or Lombok-generated getters and setters, or operator overloading where it makes sense, lead to clearer code.
Regarding IDEs: I hate Visual Studio, but JetBrains Rider is basically IntelliJ for C#.
Also, there are plenty of old, no longer working code samples on the internet for spring, but the official documentation is really great and should be preferred over some random blog that likely 6 years old already.
And can you show the nice official documentation? All my searches back then pointed me only at blogs and stuff. All of them full of outdated advice — for example, the version I started with was one of the first to change the default file extension for Freemarker from `.ftl` to `.ftlh` for some invalid reason, but none of the online blogs were updated for it, I found out after a few hours of trying out random things when I stumbled upon the change log for that release. (The reason was allegedly “security”, but what stops them from disabling the insecure features and keeping the extension?)
Another thing that was woefully under-documented was the default security thing, it also took me forever to figure out how to get a login page showing and working.
Loading all of VS2022 is not fun if you just need to turn an image file into a stream of base64.
It is very hard explaining to Java programmers why Java is so bad. Country of The Blind (https://en.wikipedia.org/wiki/The_Country_of_the_Blind) is very apropos.
C# is an incredible language that makes it easy to express a multitude of ideas from a wide range of programming paradigms.
Referencing a short story doesn't really explain it to anyone.
Recently .NET has been very good, but whenever I program in it now and then there are things I miss badly from the Java side.
From what experience I've had, C# is a very ergonomic language, with good balance between an expressive static type system and do-whatever-when-needed dynamic features.
Another language I'm looking at is TypeScript, since everything's in browser anyway, and browsers got fast.
Guess I just like Anders Hejlsberg.
This for for sure not. They do not have capable multi-platform UI.
What put me off was how cumbersome it seemed to be. Maybe I’ve just been spoiled by JS/TS, but where are the following features (which I believe to be quite basic)? - Union types - Object literals - Functions as first class citizens
I get that C# is supposed to be a purely OO language, but in this day, the most useful languages are multi-paradigm.
People like to dish on JS/TS but IMHO one reason why they became so popular is overlooked. There’s just something so direct and unceremonious about programming in JS, and with TS you get strong expressiveness as well.
Until and unless the .NET languages have that it will be hard to pull me away.
Object literals are a thing, they’re called anonymous types. You just new them up. And functions can be passed around as first class objects, with some minor type boilerplate in a few places.
There are a few places where you have to adjust to different naming conventions and types are obviously stricter but my experience is that for the most part modern c# has all the things I like about TS/JS.
Yes, these are missing in C#.
> Object literals
You could use records[1], or anonymous types[2].
> Functions as first class citizens
There's delegates[3] and lambda expressions[4]. Is there anything that's missing here that you need?
You can use lambda expressions almost exactly like you might in JS/TS, even omitting the type:
var squared = x => x * x;
And if you want even more functional, you could always use F#.[1]: https://learn.microsoft.com/en-us/dotnet/csharp/language-ref...
[2]: https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals...
[3]: https://learn.microsoft.com/en-us/dotnet/api/system?view=net...
[4]: https://learn.microsoft.com/en-us/dotnet/csharp/language-ref...
Not quite with directness of JS.
> There's delegates[3] and lambda expressions[4].
Again you can get something equivalent, but not without some ceremony. It’s not as frictionless as with JS.
I'm not sure how much less friction you want than directly declaring lambdas, or LINQ, or any of the newer functional constructs.
Like I said, if you want more functional, there's F#, which is a dialect of ML.
.NET in general has great support for 64-bit platforms and while it wasn't the default target for .NET Framework, it has been the default target for .NET [Core] for years.
We notably don't support single-dimensional arrays that have a 64-bit length or individual objects in general being that large. However, that also isn't necessarily a bad thing as there is a large difference between your overall application supporting more than 32-bits of memory (decently common and generally good) and individual allocations using more than 32-bits of memory (not so common and generally not good to have).
If you're creating individual allocations that are larger than 4GB, you're likely not correctly accounting for devices that only have 4-8GB of memory, are more prone to cause memory thrashing, and may end up negatively impacting other parts of the system since such allocations place special restrictions on how the GC can/should interact with the data or in the case of data that is or contains reference types it can add large numbers of additional references for the GC to track.
For such scenarios, it's often better to manage your memory differently, to support streaming/buffering where possible, and if it is the rare scenario that actually necessitates such large allocations to use things like NativeMemory to bypass the GC alongside Span or the SequenceReader/Writer APIs to work with smaller chunks of data. This also allows the data to be thought about differently since the algorithm you'd use to work with small to medium sized data is not necessarily the same algorithm you'd want to use when working with large to huge data. The time cost required to process multiple GB of memory is significantly different after all, as is the cache locality and other aspects.
At this point even my phone has 12GB of memory. Although I am more concerned about deep learning. Streaming tensors doesn't make much sense.
The other annoying bit is memory-mapped files.
That being said, it is largely in the same boat where if you're working with more than 2GB of memory in a single allocation, you're not necessarily going to have a good time. Even if your phone has 12GB of memory, you in practice get a much smaller portion of that available for actual usage and you don't want to cap out the available RAM either since that will cause thrashing and other issues.
75-80% of available physical RAM is about what you want to cap out at, and that's if you're the only app really using any resources.
MemoryMapped files notably support 64-bit values today and have since they were introduced. As have streams in general.
Streaming tensors can make sense and is necessary with extremely large models. The CPU cannot handle all data at once and the cost of loading everything from disk is often expensive, so you'll want to balance loading in data you need in the short term with data that isn't needed anymore or will only be needed in the long term.
Streaming or otherwise chunking your data can likewise help with parallelization and while there are some domains where chunking isn't possible, streaming always is. It just comes down to data size to determine if its beneficial or not.
https://www.wildernesslabs.co/
https://sandervandevelde.wordpress.com/2022/12/15/fun-with-n...
Plus many "embedded" scenarios have an OS capable of running regular .NET as well.
The problem of .NET versus other alternatives like Java (as an example), is the 2001 - 2014 legacy of being mostly Windows focused, which hinders the choice of libraries and tooling versus those alternatives.
For example, coming back to the embedded scenario, .NET has nothing comparable to PTC, Aicas or the endless variants of JVMs shipping on copiers, electricity meeters, M2M gateways and such.
There are a variety of development environments available—VS2022, VS Code, Rider, command-line tools. Speaking of command-line tools, the `dotnet` bootstrapper makes it really easy to get started with any sort of project.
The compiler and static analysis tool set, Roslyn, is itself increasingly written in C#, having migrated from C++.
One can write code for almost any use case (except perhaps embedded) with .NET. It really needs to be used more.