And reified generics are nice for c#, but a right pain if you want to implement another language on the CLR which isn't very c#-ish.
More recently I'm still not sure why, in a language with an extensive runtime and its own VM, they went with async / await rather than fibers or continuations like go and java. It was probably simpler to implement initially, of course.
Even slow moving java shows that bad decisions have a cost down the road even if the features are unused: there can be monitors on any object to support synchronisation, serialisation similarly lurks around internally.
Async/await work much much better than goroutines for GUI programs, which was still a major market at the time this feature was added to C#. Since there is a single GUI thread where everything that touches the GUI must live, but you do want your application to have multiple other threads, you need to give the programmer this kind of control and can't rely on a more simplistic Go-style green threads runtime.
I believe COM also has some similar requirements, but I am less sure than I am for GUI.
Finally, C# has a lot of influence from the functional langauge community, so it's possible that async/await and Task values were a more appealing solution given those particular tastes.
Isn't that just a limitation of Go since its goroutines all run in the same pool? As far as I know, in the Java implementation you'll be able to create virtual thread pools from regular threads (i.e. the gui thread) and then have another virtual executor for the rest of your program. You'll still have the backend vs gui split but you won't have the entire async await split for the rest of your application.
Couldn't you have the GUI thread on one core, and the "goroutine pool" taking all the other cores, and distribute work on that pool? The model would be a bit more complex than Go, but considering C# is made for a wider variety of usages, multiple models wouldn't be so far fetched.
No one was willing to approve another extension of the old runtime, and a major one at that. If they had already completed the .Net Core rewrite back then they would've probably baked async into the runtime.
Some C# things seemed rather strange (like delegates), some purely cosmetic (like indexers) and there were also things that Java had but C# did not like covariant return types (available from C# 9.0)
*Project Valhalla is supposed to bring it to Java but I wouldn't hold my breath waiting for it.
class Foo {
int Bar { get; set }
}
Allow you to get/set the value of someFoo.Bar as if it were a public member.
If you want read- or write-only members, you just remove the "set" or "get" portion.
If at some point you need to add logic to the setter, you just do int Bar { get; set { /* setter logic goes here */ } }
You still use assignment syntax ("someFoo.Bar = newValue"), but this syntax is desugared into a call to the setter method. Ditto for the getter.IMHO they make class APIs much cleaner and forward-compatible (on the other hand, if you started with a public member and then need to add a proper setter with some logic, you would break the API).
Also, non-nullable types are a pretty nice way to avoid checking for null every other line of code.
And these are literally just the first couple examples that came to mind.
I haven't developed in Java in a long time, but public instance variables were a no-no when I did for these reasons.
This isn't the only way of doing it, depending on your language. Python has a completely different mechanism where a class can start with a directly-accessed "y", but in a later version make it a property. (More than one, actually.) Some languages route all property accesses through something you can hook. Some languages simply automatically do what C# is doing here.
Semantically and abstractly, there's no reason to every not be able to override a property access in later versions like that, because it is so darned useful. Unfortunately, performance wise, it's hard to beat a language that guarantees structure layout and guarantees that x.y can be compiled into a single constant memory load instruction, and unfortunately, you can't just wave "compiler optimization" at the problem and make it go away (considered in isolation this seems like an obvious solution but as usual as it starts interacting with all the other features it gets much harder than the simple cases). The higher performance languages have a strong reason to give people ways to get guaranteed direct struct member accesses.
Non-nullable types sound great, I wonder if this could be introduced to Java in a backwards-compatible way (I suppose it would follow the same path with introducing explicitly nullable types first, though I guess it could be handled using Optionals?).
The code compiles but from time to time it fails at runtime and performance are sometimes Ok, sometimes not depending on the call stack.
[1] https://docs.microsoft.com/en-us/dotnet/csharp/programming-g...
This is how I feel about Kotlin.