8 ms·
The best thing about .NET over Java is the ability to define custom struct types. In Java, every object is heap allocated - unless you are lucky enough for tha
by dfgdghdf 6y ago
The best thing about .NET over Java is the ability to define custom struct types.
In Java, every object is heap allocated - unless you are lucky enough for that to be optimized away. If you absolutely need the performance, you must resort to a weird kind of place-orientated programming, where you pass around mutable references to primitive types that get operated on by static functions. It kind of resembles C, but without the struct keyword!
- taspeotis 6y agoYes, everything is on the heap! https://codegolf.stackexchange.com/a/28818 https://codegolf.stackexchange.com/a/28818
- Ygg2 6y agoAs a Java-lander, programming C# for fun, while I like struct as a concept they are horrible in practice. What do I mean by that? Here's my idea. Make a Fluent parser. I have some few shallow structs that can be used interchangeably. E.g. you want to have following structure Entry: - Message - Term - Comment How do I add polymorphism to my entries? Option 1) Go with interfaces. Yay. I've incurred the boxing penalty. I might as well as just write a class. Option 2) Go with [FieldOffset]. Only do this if you are a masochist and you REEEEEEEEEEEEAAAAAAAALLY need that performance. In Rust for instance, I could replace that stupid struct with an enumeration, which is impossible in C#. Hopefully in future there will be discriminated unions or some Rust enums equivalent.
- virgilp 6y agoThis seems quite a leap from "C# doesn't (yet) have union types" to "structs are horrible in practice". Not all usecases require union types.
- Ygg2 6y agoStructs are often horrible in practice if you need even a bit of polymorphism. If C# had the ability to define List< struct A | B | C > I would been singing a different tune. You either in for List<IInterface> or List<ABCFieldOffset>
- zigzag312 6y agoThis would be beautiful
- csharptwdec19 6y agoThere's a few challenges with doing something like this. - arrays (which most collections use under the covers) will need to allocate `elements*size_of_struct`. Unless the structs are the same size, you'd have to do some level of thunking/offsetting. - Even if they are the same size, when you're iterating the list, how would the runtime know whether it's `A`, `B`, or `C`? Structs don't have header information that can be used to infer the type, it's only whatever fields you've defined (with padding potentially). Part of 'boxing' is essentially thunking the struct by wrapping a methodtable for the struct into the object [0]. If you need something like polymorphism with structs, your best bet is to make the struct itself quasi-polymorphic (i.e. with enums) and/or start abusing generics in some fashion I haven't yet figured out. [0] - https://mattwarren.org/2017/08/02/A-look-at-the-internals-of-boxing-in-the-CLR/ https://mattwarren.org/2017/08/02/A-look-at-the-internals-of...
- Ygg2 6y ago> Structs don't have header information that can be used to infer the type, it's only whatever fields you've defined (with padding potentially). Sure, so let's make union or "struct enum" and do the same thing Rust does. Add a discriminator byte to the structure and use it to figure out type. > If you need something like polymorphism with structs, your best bet is to make the struct itself quasi-polymorphic (i.e. with enums) and/or start abusing generics in some fashion I haven't yet figured out. Well. I think there is the FieldOffset hack. Basically discriminated unions where programmer plays the role of compiler. [StructLayout(LayoutKind.Explicit)] struct DiscriminatedUnion { // Imagine we have enum like so // enum // { // V1(byte a, byte b) // V2(sbyte a, sbyte b) // } // The "FieldOffset" means that this Integer starts, an offset in bytes. // sizeof(byte) = 1, sizeof(EnumType) = 1 [FieldOffset(0)] public EnumType TypeFlag; [FieldOffset(1)] public byte V1a; [FieldOffset(2)] public byte V1b; [FieldOffset(1)] public sbyte V2a; [FieldOffset(2)] public sbyte V2b; } https://sodocumentation.net/csharp/topic/5626/how-to-use-csharp-structs-to-create-a-union-type---similar-to-c-unions- https://sodocumentation.net/csharp/topic/5626/how-to-use-csh... Problem is, this is fragile. It might break on endianess.
- gpderetta 6y agoAs a C++-lander, the fact that the struct/class dichotomy dictates boxing and polymorphism is mind boggling.
- josefx 6y agoIn C++ struct and class are the same except for default visibility rules. You could drop either keyword from the language and it wouldn't make any practical difference outside of C compatibility. In GCed languages the default seems to be that every object is referred to by pointer. Java took the "ugly" step of adding in primitive types so you could calculate 1 + 1 without causing dozens of cache misses. Struct types take this hack a step further. It generally makes the definition of the languages virtual machine a lot uglier, for example most jvm instructions operate on primitive types.
- Ygg2 6y agoWell I understand why it's there. Structs are just that data stored in the same layout. Classes are just a vtable pointer to some object on heap.
- mjburgess 6y agoIt's because "class" and "class" are referring to two difference semantics. An OO GC'd language "class" is a mechanism for run-time polymorphism. That's what it's for. C++ has no "runtime" concerns of these kinds, so virtualization sits on top systems with compile-time semantics (ie., c++ classes).
- 6y ago
- dfgdghdf 6y agoYou are writing C# like it's F#. In F# you can write union types that are also structs: [<Struct>] type Entry = | Message | Term | Comment And like in Rust, you have pattern matching on them: let computeFooForEntry entry = match entry with | Message -> 0 | Term -> 1 | Comment -> 2
- Ygg2 6y agoI'm not trying to. It's just a shallow type hierarchy. And I can achieve it with Interface. But I would prefer a solution that doesn't box.
- dfgdghdf 6y agoInterfaces don't have a compile-time size so I don't see how you can use avoid boxing if you want a collection of IFoo. (Optimized) discriminated unions have a size that is the max size of the possible types, plus a flag to indicate the type. This is the best you can hope for without boxing.
- Const-me 6y agoInterfaces don’t always have boxing penalty. Write generic code, use the interface as generic type constraint, and the interface will work without virtual calls, boxing, or any other runtime overhead. Example of that approach: https://github.com/Const-me/Vrmac/blob/1.2/Vrmac/Draw/Main/ImmediateContext.upload.cs#L86-L137 https://github.com/Const-me/Vrmac/blob/1.2/Vrmac/Draw/Main/I...
- TriNetra 6y agoAnother benefit of struct is since enumeration can't be inherited, you can achieve something similar with struct and operator overload magic as I did with OpResult [0], to allow consuming applications of ASPSecurityKit extend possible OpResult values with their app specific failure codes, without having to do ugly explicit casting if an enum was used instead. 0: https://aspsecuritykit.net/docs/api-reference/aspsecuritykit/opresult/ https://aspsecuritykit.net/docs/api-reference/aspsecuritykit...
- pjmlp 6y agoIt is coming, https://openjdk.java.net/jeps/404 https://openjdk.java.net/jeps/404 https://openjdk.java.net/jeps/402 https://openjdk.java.net/jeps/402 Both .NET and Java did major mistakes at their v 1.0, by ignoring what other GC based languages were doing. - Proper value types (Even .NET is only catching up since C# 7.0) - AOT compilation alongside JIT (NGEN was just for startup and without optimizations, and on Java side only third parties) - Seamless FFI with host OS (P/Invoke did better, JNI is finally getting a replacement) Eiffel, CLU, Mesa/Cedar, Oberon linage, Modula-2+, Modula-3 were inspiration, but those capabilities were apparently considered not relevant enough, go figure.
- thu2111 6y agoI don't think those things are necessarily mistakes. Maybe JNI was but most languages took the same approach of exposing interpreter APIs instead of providing FFIs to C. AOT compilation of a dynamic language like Java is hard to do without losing performance. You can AOT Java now with the same compiler used also for JITC (Graal) and you lose, I think, about 20% of the peak performance unless you do C++ style profile collection and double compilation. It's not clear that lack of AOT compilation held Java back any - although SubstrateVM/native-image are getting popular at the moment, that's taking place in the context of hype around "serverless functions". If you aren't using those (or writing small CLI apps, or doing other unusual things) the benefit is less clear. With respect to proper value types, trying to do that in 1.0 would have just killed the projects. Biting off too much. Look at the complexity of Valhalla - only a small part of it comes from backwards compatibility. Most of it is trying to get the benefits of generics over value types via specialisation without the horrible consequences suffered by other languages. It's not about rectifying past errors but rather, doing something new.
- gher-shyu3i 6y ago> dynamic language like Java Java is a static language.
- kaba0 6y agoStatic in itself doesn’t have a strict definition. You are right that for example java is statically (and strongly) typed, but it has many dynamic elements like class (re)loading, reflection.
- MrBuddyCasino 6y agoTrue. Project Valhalla (which would add this to the JVM) has been years in the making, not sure why it takes so long. But at long last it seems to come to fruition: https://wiki.openjdk.java.net/display/valhalla/Main https://wiki.openjdk.java.net/display/valhalla/Main
- pjmlp 6y agoJava doesn't want to play a Python 3 and ABI compatibiliy is relevant. Your 25 year old jars are supposed to keep working as much as possible.
- reader_mode 6y agoWhen was this true ? I remember since forever the "DON'T UPGRADE JVM ON THIS SERVER" notices since Java 5-6 days. Hell I've seen a case where a minor update would break a production app. And I don't even do Java development. Not to mention android and gradle being broken and randomly failing to work between different JVM versions - depending on the gradle flavour of the day. I don't really see JVM as a pillar of stability and backwards compatibility.
- martontoth 6y agoThats not a JVM, but an application/library level issue. The JVM ABI has been stable for quite some time.
- reader_mode 6y agoI see limited value in that when JVM ships with frameworks which break compatibility regularly - I doubt Python packages which had no dependencies on core libraries had any problems using 2to3 to migrate.
- overtomanu 6y agocan you give examples?