Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

> The Unity engine has evolved a lot in modern days, but I noticed a trend where Unity developers are still using "outdated" techniques when writing their C# code.

Some years ago I tried to get into C# + Mono. Eventually I opted for Java instead, for many reasons; I'll skip that here.

C# is very strange to me. In a way I feel that C# belongs like Java in the same "post C++" family; C kind of paved the way, C++ was messy and powerful, so Java and C# would be more "managable". But I never got into C#. Java is not a pretty language, it is also quite boring, but modern Java is somewhat acceptable - you get the job done. And it is not an extremely difficult language either for the most part, just with an addiction on pointless verbosity. C# is ... strange though. TIOBE has it ranked #5 right below Java, so there must be many C# users, but I don't get to see them really in the Linux ecosystem. So where are these people all? Using Windows only? When the question is "most developers don't use feature xyz", do all of them actually KNOW these features? You can still find many java tutorial where people use archaic ways to, for instance, iterate over a collection. Perhaps it is similar to the C# ecosystem, people are slow to adopt. Or, and this may also be a reason, people could have moved to other languages. This may not be a huge percentage, but you see that some languages suddenly struggle with old devs and failing to get new devs (ruby is in this problem right now; it may overcome it but right now it is sinking hard, even though I would reason that the language is, for the most part, better than it was in, say, 2010).



C# has had the reputation of not being viable for Linux for a long time. Therefore, the people already on Linux didn't have a reason to use it or even try it. If you're already doing stuff in other languages it's hardly worth it to switch to C#.

I personally use it quite a lot - but I came as a windows user writing all my utilities in C#. Also, afaik C# is mostly used in corporate environments that don't open-source their projects. You're unlikely to hear from it unless you're working on it for this very reason.


Mono was in a usable state on Linux for literal decades before becoming official and integrated into what is core today, that is unless you needed windows forms, which much like MSFT UI frameworks today had multiple failed attempts spanning those same decades...


Only if you didn't care about performance.

We attempted a move to mono for backend web services maybe 3 years before .NET Core released, and it was a complete no-go. 10x reduction in performance on the same hardware.

This wasn't specific to our workload either. I was big into the game Terraria at the time, and I saw similarly poor performance when running it's game server under mono vs .NET 4.x

While some of the mono toolchain was integrated into .NET Core, CoreCLR was a rewrite and immediately solved this problem.


Great context, thanks! I knew it worked (I closely followed mono development at the time) but I didn't have a windows license/windows machines at the time it was ongoing to compare it to. 4x is pretty bad, any ideas what went wrong? Lack of jit maybe? I forget the exact architecture of mono at that time, being like 20 years ago...


It did have a JIT, it just wasn't a very performant one. I recall the GC implementation also being quite slow. GC is an area where .NET is still making strides.. we got a new collector in .NET 10.


A lot of C#'s reputation for not being viable for Linux came from the other direction and a lot of FUD against Mono. There were a lot of great Linux apps that were Linux first and/or Linux only (often using Gtk# as UI framework of choice) like Banshee and Tomboy that also had brief bundling as out-of-the-box Gnome apps in a couple of Linux distros before anti-Mono backlash got them removed.

Also, yeah today Linux support is officially maintained in modern .NET and many corporate environments are quietly using Linux servers and Linux docker containers every day to run their (closed source) projects. Linux support is one of the things that has saved companies money in running .NET, so there's a lot of weird quiet loyalty to it just from a cost cutting standpoint. But you don't hear a lot about that given the closed-source/proprietary nature of those projects. That's why it is sometimes referred to as "dark matter development" from "dark matter developers", a lot of it is out there, a lot of it doesn't get noticed in HN comments and places like that, it's all just quietly chugging along and doesn't seem to impact the overall reputation of the platform.


Yes, however as acknowledged by the .NET team themselves, in several podcast interviews, this is mostly Microsoft shops adopting Linux and saving Windows licenses.

They still have a big problem gaining .NET adoption among those that were educated in UNIX/Linux/macOS first.

Mandy Mantiquila and David Fowler have had such remarks, I can provide the sources if you feel so inclined.


C# has a unique powerful position in the video game space. In almost every other niche, there are better (or just trendier) solutions, but C# is the only major language that actually gives you a combination of features that are important in video games:

- Dynamic runtime with loose coupling and hot reload of code - extremely useful during development.

- Value types. You don't want every Vector4 to be heap allocated when you're doing 3D graphics, because that's going to be absolutely unusable.

- Access to a relatively low-level substrate for basically-native performance when needed - extremely useful when you're trying to actually ship something that runs well.

Taken in isolation, C# isn't best in class for any of them, but no other language offers all three, especially not if you also want things like a really good debugger and great IDE tools.

To my knowledge, Java has none of these features (yet), and they aren't really important in a lot of the areas where Java is popular. But this is why C# in particular is very strong in the video games niche.


> no other language offers all three

Julia. Of course with the added downside that it's not deployable (asterisk here), which is somewhat important for games. IDE and debugger could be better, but at least it doesn't insist on classes like C#.


I think these are all valid arguments, but I do want to point out that Java is addressing them.

The first bullet is possible with the JetBrainsRuntime, a fork of OpenJDK: https://github.com/JetBrains/JetBrainsRuntime

The second bullet is a headline goal of Project Valhalla, however it is unlikely to be delivered in quite the way that a C# (or Go or Rust etc.) developer might expect. The ideal version would allow any object with purely value semantics [1] to be eligible for heap flattening [2] and/or scalarization [3], but in experimental builds that are currently available, the objects must be from a class marked with the "value" qualifier; importantly, this is considered an optimization and not a guarantee. More details: https://openjdk.org/projects/valhalla/value-objects

The third bullet (IIUC) is addressed with the Foreign Function & Memory API, though I'll admit what I've played around with so far is not nearly as slick as P/Invoke. See e.g. https://openjdk.org/jeps/454

[1] value semantics means: the object is never on either side of an == or != comparison; the equals and hashCode methods are never called, or are overridden and their implementation doesn't rely on object identity; no methods are marked synchronized and the object is never the target of a synchronized block; the wait, notify, and notifyAll methods are never called; the finalize method is not overridden and no cleaner is registered for the object; no phantom or weak references are taken of the object; and probably some other things I can't think of

[2] heap flattening means that an object's representation when stored in another object's field or in an array is reduced to just the object's own fields, removing the overhead from storing references to its class and monitor lock

[3] scalarization means that an object's fields would be stored directly on the stack and passed directly through registers


The third bullet is also presumably referring to C#'s ancient wider support for unsafe { } blocks for low level pointer math as well as the modern tools Span<T> and Memory<T> which are GC-safe low level memory management/access/pointer math tools in modern .NET. Span<T>/Memory<T> is a bit like a modest partial implementation of Rust's borrowing mechanics without changing a lot of how .NET's stack and heap work or compromising as much on .NET's bounds checking guarantees through an interesting dance of C# compiler smarts and .NET JIT smarts.


The FFM API actually does cover a lot of the same ground, albeit with far worse ergonomics IMO. To wit,

- There is no unsafe block, instead certain operations are "restricted", which currently causes them to emit warnings that can be suppressed on a per-module basis; it seems the warnings will turn into exceptions in the future

- There is no "fixed" statement and frankly nothing like it all, native code is just not allowed to access managed memory period; instead, you set up an arena to be shared between managed and native code

- MemorySegment is kinda like Memory<T>/Span<T> but harder to actually use because Java's type-erased generics are useless here

- Setting up a MemoryLayout to describe a struct is just not as nice as slapping layout attributes on an actual struct

- Working with VarHandle is just way more verbose than working with pointers


> - There is no unsafe block, instead certain operations are "restricted", which currently causes them to emit warnings that can be suppressed on a per-module basis; it seems the warnings will turn into exceptions in the future

Which sounds funny because C# effectively has gone the other direction. .NET's Code Access Security (CAS) used to heavily penalize unsafe blocks (and unchecked blocks, another relative that C# has that I don't think has a direct Java equivalent), limiting how libraries could use such blocks without extra mandatory code signing and permissions, throwing all sorts of weird runtime exceptions in CAS environments with slightly wrong permissions. CAS is mostly gone today so most C# developers only ever really experience compiler warnings and warnings-as-errors when trying to use unsafe (and/or unchecked) blocks. More libraries can use it for low level things than used to. (But also fewer libraries need to now than used to, thanks to Memory<T>/Span<T>.)

> There is no "fixed" statement and frankly nothing like it all, native code is just not allowed to access managed memory period; instead, you set up an arena to be shared between managed and native code

Yeah, this seems to be an area that .NET has a lot of strengths in. Not just the fixed keyword, but also a direct API for GC pinning/unpinning/locking and many sorts of "Unsafe Marshalling" tools to provide direct access to pointers into managed memory for native code. (Named "Unsafe" in this case because they warrant careful consideration before using them, not because they rely on unsafe blocks of code.)

> MemorySegment is kinda like Memory<T>/Span<T> but harder to actually use because Java's type-erased generics are useless here

It's the ease of use that really makes Memory<T>/Span<T> shine. It's a lot more generally useful throughout the .NET ecosystem (beyond just "foreign function interfaces") to the point where a large swathe of the BCL (Base Class Library; standard library) uses Span<T> in one fashion or another for easy performance improvements (especially with the C# compiler quietly preferring Span<T>/ReadOnlySpan<T> overloads of functions over almost any other data type, when available). Span<T> has been a "quiet performance revolution" under the hood of a lot of core libraries in .NET, especially just about anything involving string searching, parsing, or manipulation. Almost none of those gains have anything to do with calling into native code and many of those performance gains have also been achieved by eliminating native code (and the overhead of transitions to/from it) by moving performance-optimized algorithms that were easier to do unsafely in native code into "safe" C#.

It's really cool what has been going on with Span<T>. It's really wild some of the micro-benchmarks of before/after Span<T> migrations.

Related to the overall topic, it's said Span<T> is one of the reasons Unity wants to push faster to modern .NET, but Unity still has a ways to go to where it uses enough of the .NET coreclr memory model to take real advantage of it.


Yeah, coming to C# from Rust (in a project using both), I’ve been extremely impressed by the capabilities of Span<T> and friends.

I’m finding that a lot of code that would traditionally need to be implemented in C++ or Rust can now be implemented in C# at no or very little performance cost.

I’m still using Rust for certain areas where the C# type system is too limited, or where the borrow checker is a godsend, but the cooperation between these languages is really smooth.


> I don't get to see them really in the Linux ecosystem

Sounds like you're just in a part of the ecosystem that doesn't touch C#. Aside from gamedev, C# is mostly a back-end web language, and almost all new builds are going to deploy to some Linux based serverless offering or something like Kubernetes. At my work, we're a mix of Python/Typescript/C# deploying to Google Kubernetes Engine and Cloud Run.

As for the language, your parallels to Java are accurate but outdated. Older versions of C# were very very similar to Java, but C# progressed when Java didn't. For better or worse (depending on your tastes), C# is more comparable to Kotlin or Swift these days than Java.

That's to say nothing of C#'s contributions to other programming languages; async/await and unsafe{} blocks were both first seen in C# before being adopted elsewhere.


We are deploying .NET (Core) on Linux since soon 10 years. With hundreds of devs and dozens of services on thousands of servers.

We have not touched a Windows server in years.

In 2026 .NET is deployed like anything else into Linux containers and Lambdas. Obviously there are still people who love their ui based servers but that is because of that and not because of .NET.

Adoption of new features is gradually and explorative, obviously in an enterprise everything is on a LTS 2-3 back but adoption after that delay is rather quick to my observation.

.NET like Java have this "it is there and we extend it. And the new platform is also .NET because you have your dozens of devs already"


There is a huge amount of closed source, ecommerce shops that use C#. Its popular in game dev too but TIOBE is probably attributable to the ecommerce.

If you know Java, you should give C# a try. Its a slicker Java with some good decisions that actually make it viable for a lot of things Java struggles at, like better interop with pointers and things like native UI (Maybe Loom will eventually overtake async/await for UI dev but not quite yet).

It works very well on many more use cases than it used to. .NET has followed with the Linux servers in the cloud industry reality and is very stable.

Its just a good language. Try it out.




Consider applying for YC's Summer 2026 batch! Applications are open till May 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: