6 ms·
What stuff does C# objectively do better than Java?
by bcrosby95 4y ago
What stuff does C# objectively do better than Java?
- solarkraft 4y agoCan't really be objective here. You could also argument this makes it worse, but it gives you to take shortcuts for things Java would make very hard or cumbersome (unsafe code, properties).
- titzer 4y agoGenerics, LINQ, structs, delegates, to name just four.
- hhjinks 4y agoCan you explain why you think C# does those things better than Java? And what they correspond to in Java?
- titzer 4y agoSure. Reified generics, along the lines of C#[1], rather than Java, is the only sane design. Erasure of generics works fine in languages that have the "parametricity" property, where you cannot observe any characteristics of a polymorphic value. That doesn't hold as soon as you have casting. Java generics have a couple of unfixable problems. For one, you can have List<Foo> and List<Bar> both be passed to a place where Object is expected. But getting them back from that place and trying to recover what was known at creation time, would normally be done with a cast. But List<Foo> and List<Bar> are not runtime distinguishable from each other. Such a polymorphic cast in the code is just syntactic sugar; you'll get a compile-time warning, but no runtime check! E.g. You can get a naked List and cast it to a List<Bar> and then go ahead and try to use it like a List<Bar>. It will only fail when you get an actual non-Bar thing out of the list. It pointedly won't fail if you take your List<Bar> and pass it to other generic code that does more generic stuff with it. And it won't fail if the list is full of nulls, is empty, etc. In essence, it's completely dynamically typed at that point. The static generic types are lies. In reality, all generic Java code compiles down to non-generic code with runtime casts everywhere. That costs performance and means that you can screw up. Second, erasure also means that you cannot be polymorphic over primitive types in Java, as the VM doesn't support that in the bytecode. So you can't write really basic stuff like Vector<T>, array sorting code, and now, closures and lambdas, that manipulate primitives. All that has to be duplicated, once per primitive type, and for objects. Or you have to box stuff. So they added implicit boxing (autoboxing) so you don't have to type those characters. But they are there in the code. You're stuck with either duplicating for performance (hand-doing template specialization, if you will), or just creating an assload of garbage. [1] I say "along the lines of". I designed Virgil's generics system not knowing C#, but working from what I knew from ML. It ended up with a lot of the same choices, but I mapped ML's "unit" onto "void" and that works out nicely for zero-arg and/or zero-return functions. You can even have an Array<void> in Virgil! And none of it creates boxes or introduces unsafe casts.
- debug-desperado 4y ago"Static generic types are lies" yes, and it doesn't matter that much. You got the safety at compile time and if you didn't do an unsafe cast then you preserved it. The issue with primitive types and boxing is certainly noted. Hopefully Valhalla will address it and more. The problem with reified generics is that the same variance model must be adopted by all guest languages on the runtime. Hence you basically don't see any guest languages on the CLR, and efforts by languages such as Scala to port to the CLR failed due to problems interoping with C#. I think one of the JVM engineers "pron" has discussed this multiple times.
- titzer 4y ago> The problem with reified generics is that the same variance model must be adopted by all guest languages on the runtime. I think this is a fair observation, but it really boils down to "a dynamically typed compilation target is an easier target", which isn't all that surprising. > such as Scala to port to the CLR I am not as familiar with Scala's saga here, but I've heard multiple conflicting reports from Scala insiders, so I think this is a more complicated issue than just the generics model of the CLR.
- kaba0 4y agoYour first point is kind of making a big deal out of something that isn’t that big of a deal. The trivial solution is to just.. use the language’s static typing and don’t pass typed objects as Object. With a generic function nigh everything can be done type-safely (remember, if you don’t use class casts, your generic code will be completely type safe). Sure, there are some edge cases, but they are so edge-cases of an edge case that I really don’t think it is a great differentiator between the two platforms. Your second point is true though, and the fix is something that requires at least 6 PhD’s combined, but they are working on it.
- titzer 4y agoI mean, I agree. The static warnings will generally steer you away from the bad cases. From a source-language standpoint, if you don't consider the second-order effects of this design choice, it accomplished a fair fraction of the original goals. But more complex patterns I've seen people try to pass around java.lang.Class objects as a reification. That doesn't really work, because it's skin deep. With significant experience with reified generics, there's just a lot of patterns that you can't do in Java. I wrote a whole PLDI paper about it back in 2013. Reified generics mean you can do stuff like ad-hoc polymorphism without direct language support. Ironically enough, the little trick of hiding type arguments with subtyping but getting them back with casts is a powerful dynamic tool.
- babypuncher 4y agoBenchmarks generally show C# outperforming Java, but not to a degree that most people would worry about. Performance-sensitive applications are still going to go for something native. One of C#'s biggest assets is Anders Hejlsberg of Turbo Pascal fame. He's been in charge of C# since its inception. He's done a very good job of keeping the language clean and concise, and generally ahead of Java when it comes to adopting features like generics and functional programming.
- kaba0 4y agoI would put it differently: C# has a higher performance ceiling due to it having access to lower level controls (mostly value types), but for idiomatic code, there really is no such difference (and if there is any, I would say it goes towards Java). Also, for programs having very dynamic allocation patterns, Java’s GCs are the state-of-the-art and are basically impossible to beat.
- runevault 4y agoI thought Anders was full time on Typescript these days? Certainly his fingerprints are all over c# from being in charge for a long time though.
- babypuncher 4y agoHe still acts as the lead architect on C#, in addition to working as the core developer on TypeScript.
- tejinderss 4y agoNope, Mads Torgersen is lead architect on c# nowadays, Anders is 100% on Typescript's typechecker.
- babypuncher 4y agoSounds like someone's Wikipedia page needs to be updated then
- cutler 4y agoBetter lambdas and more recently null safety.
- kinjba11 4y agoI'd say the two big ones are: - Mature support for async/await since 2012. Yes, Project Loom is coming, a decade later.. - Support for generic-aware value types (struct vs. class) and low-level features like stackalloc: very valuable for high-performance scenarios and native FFI. See for instance https://github.com/ixy-languages/ixy-languages https://github.com/ixy-languages/ixy-languages. In comparison, Java doesn't even have unsigned integers. Yes, Project Valhalla is coming someday. As well, debatable to some folks, but: properties (get/set); operator overloading; LINQ > Java streams; extension methods; default parameters; collection initializers; tuples; nullable reference types; a dozen smaller features
- kmac_ 4y agoLet me continue: no type erasure, type safe generics, switch case guards, native tuples, async/awiat in loops and lambdas, the language becomes more and more functional with every version, and on top of that, .NET Framework has quite coherent API/interfaces and tooling is much better. The single type erasure is a huge difference that makes those two languages quite different.