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

"Can be tempting to think of it as a Java clone".

C# is superior to Java in every way I can think of. Just a couple examples, unsigned types, simple value types without boxing, and a real generics implementation (java generics are truly awful). The reflection API is c# is also far more useful and easy to understand.



As a language, yes C# is probably better. If I may grant Java a pithy defense as a C# user not as a contradiction but additional information.

On the implementation side, Hotspot blows the CLR out of the water on performance. All things being equal that is, not handicapping the Java code, bottlenecks "for realism" and other benchmark-defeating tricks.

Java is the superior serverside solution. Other than Hotspot performance, the other big selling point is Java's vastly larger opensource library selection.

I like both. I like the fat OOP space. Maintenance reasons is a huge one, I don't want to get burned having to maintain some fly-by-night's code in 10 years from a Frankenstein's creation of random JS libraries. Both Java and C# are industrial-strength designs, with good tooling and a swath of devs.

Between the two, I'd still reach for C# myself, because at this point you can have an almost entirely C# codebase from the server to the client. It's good enough in the performance and library aspects that Java holds over its head. I'm expecting the C# package to be quite complete once it adds compiling to wasm to its list. But if I made a career switch, it would be from C# to Java, and nothing else. I admire Rust and (especially) Elixir but no jobs in my area for either of those.


Java is faster for high-level code. The moment you start using structs and stackalloc and whatnot, Hotspot cannot really keep up.

Even for high-level code, it depends on the kind. E.g. Java generic collections have to box value types; I'm not sure if Hotspot even tries to avoid it - I think it does these days - but this kind of analysis is necessarily limited in scope. In C#, value type generics are fully reified, even on JIT level - every combination of type parameters gets its own JIT-compiled implementation.


I didn't know that. That's beneficial for me to know and makes sense.

I recently made my career change to become a C# developer so I did a good indepth review on the overall landscape. Came down to Java and C# as my best bets, C# won because it fits a lot of my work history and I've heard multiple people that I trust say they just can't find .Net developers. To me that's a feature. I really like it and don't understand all the hatred and fashion-oriented programming trends going on. Honestly, I'm more about maintenance and all the best tooling to help me get complex tasks completed without issue. Of course, maintaining employment is important too, has to be jobs around. I spoke with a few companies, one is switching from a .Net stack to Node and I think that's insane. I'm not sure why you'd actually willingly accept a downgrade like that. Maybe if you're starting from scratch, sure do what you will, but overall I expect the "app" side to JS to dwindle away as wasm takes over. I intend to build most of my webpages leveraging as much of the native browser functionality as possible, leaving apps to Xamarin and wasm for native code solutions.


That is just temporary glitch, until Java gets value types.

The biggest problem is that no-one wants to do a Python3 on the pile of Java code written in the last 20 years, so of course that have been doing baby steps, which are starting to see the light now post-Java 9.

And they will come, because Java is feeling the pressure on Fintech from companies that want to move away from C++, but still feel some pain, ergo Pony.

Java also needs such features for Project Metropolis, the JVM can only be successfully rewritten in Java, if there are no performance regressions.

Also, IBM and Azul JVMs do have language extensions for value types.


Well, it's been a temporary glitch for, what, 13 years now?

Yes, Java will fix this eventually (although, doing this for generic collections would also require reified generics, no?). But, of course, C# will also advance further in that time.

The general problem, I think, is that the process that Java uses for language evolution is deliberately slower than C#, so C# can bring new features faster. There's a trade-off there in that there's also less breakage, and acquired Java skills take longer to become obsolete. Which probably has something to do with why Java is still very prominent in the enterprise, and in industries like banking.


Well, C# did not switch hands among a company acquisition, whose owner lost sight how to move forward the language.

Even C# does move slowly, these lovely C# 7.2 features that I can already use on my private projects, will take years to be allowed on my typical set of customers, the enterprise.

I just helped one transition to .NET 4.6.1.

Likewise another one just moved into Java 8 this year.

Worse is the Android situation, regardless of the features Java might get, even feature parity with C#, Google will cherry pick only the ones they care about.


The nice thing about many new C# features is that they are still supported on older runtimes. So you can ship stuff that runs against 4.5 or even 4.0, while using e.g. the new property syntax, or inline out, or even pattern matching (for your own classes). For something like tuples that needs library support, they now provide the requisite bits as NuGet packages - if I remember correctly, the one for tuples is also 4.0 up.


Yeah, but that is a side effect of the CLR original target, as a means to support any language, including C like ones.

Which is ironic that nowadays JVM gets more languages, with compiler backends pretending to be Java like, while .NET SDK 1.0 even had multiples examples of programming languages.


Java enums are better. Seeing as C# is going to run on more than Windows that's about all I can think of.


Anonymous inner classes are more powerful than c# anonymous types. I think c# wins out overall, but there are a few corners where Java has features c# is missing, coming from the fact that c# was a bit more pragmatic where Java was a bit more oop-purist in its origins.


Anonymous delegates (which have been available since 2.0) are the equivalent feature in C#, at least as far as use cases go.


Delegates are nice in the common case but I do miss anonymous classes.

Anonymous classes have better oo organization than passing in two or more delegates. Sometimes variable capture doesn't cut it or isn't as clear.

..And the biggest thing. I can pass it in to a method that already takes a class and not a delegate.


> And the biggest thing. I can pass it in to a method that already takes a class and not a delegate.

One solution is the build a small inventory of "ad-hoc" classes that just wrap a delegate (usually a closure), as described here: https://stackoverflow.com/questions/20703002/does-object-exp...


While anonymous inner classes are more verbose, they're also more powerful because you can create a whole class instead of a single method.


But, they're equivalently powerful with respect to an API.

In Java, a method might expect one instance of a class, to which you can pass an instance of anonymous class, whereas in C# a method might expect a set of delegates, to which you can pass a set of anonymous delegates or lambda expressions.

However, thanks to C#'s type inference, it actually ends up being far less verbose, yet more flexible, because anonymous delegates and lambdas can close over local variables. So, strictly speaking, they're more powerful than anonymous inner classes.

On the other hand, as someone that greatly admires Smalltalk, I sometimes envy Java's slightly better purity.


During Java early days, I was a big fan of Eclipse's Java browsing perspective, it was a way to feel like I was still using Smalltalk.


What are the features that c# is missing? As the other comment explained delegates are the equivalent of anonymous inner classes and are far better. Checked exceptions are really a nuisance, I would call them a negative feature. Java enums in c# are basically classes with readonly properties if you need to pass constructor arguments. Streams in Java are a huge clusterfuck. I can't think to anything that Java has that is better than c#.


Delegates are not the equivalent of anonymous inner classes. They're commonly used for similar design patterns (observer and other callbacks), yes, but they're not the same thing. To be more specific, an anonymous delegate / lambda can be thought of as an implementation of an interface with a single method. But with Java anonymous inner classes, you can implement multiple methods at once.

The reasons why Java enums are better, is because they're actually constrained to the domain you specify. In C#, the range of values for any enum is the same as its underlying integer type - it's just that some of those values have names, while others don't. But it's always legal to cast (and few people know this, but 0 is always a valid enum value that you don't need to cast) - so any method you write that accepts an enum value has to validate it. In Java, since enum is basically a final class with a bunch of private singletons, you are guaranteed that the reference you get points to one of those.

That said, given that C# now has pattern matching, and it seems to be getting more powerful with every release, I'd be surprised if it didn't get something like case classes very soon.


Delegates and anonymous inner classes are equivalent, modulo API:

    //Java
    
    Bax.addHandler(new Foo()
                   {
                       @Override
                       public void Bar()
                       {
                           doSomething();
                       }

                       @Override
                       public int Baz(X n)
                       {
                            return computeInt(n);
                       }

                   });
    
    //C#

    Bax.AddHandler(() => DoSomething(), n => ComputeInt(n));

But, anonymous delegates and lambdas are at once syntactically nicer and more powerful, because of type inference and the fact that they can capture local variables.

Regarding Java enums, the actual equivalent feature in C# is class nesting. There is a tiny bit more ceremony involved in defining them, but they're more flexible than Java enums. For instance, you can decide whether or not to implement them as readonly (final) fields, or as static properties, depending on how much you care about cache friendliness.

    public abstract partial class SomeEnum
    {
         private SomeEnum(){}

         public abstract int Biz();

         sealed class AFoo : SomeEnum { public override Biz() => 2 }
         sealed class ABar : SomeEnum { public override Biz() => 4 }         
         sealed class ABaz : SomeEnum { public override Biz() => 8 }         
  
    }

    //Singleton implementation:

    public abstract partial class SomeEnum
    {
        public static readonly SomeEnum Foo = new AFoo();
        public static readonly SomeEnum Bar = new ABar();
        public static readonly SomeEnum Baz = new ABaz();
    }  

    //Cache friendly implementation:

    public abstract partial class SomeEnum
    {
        public SomeEnum Foo => new AFoo();
        public SomeEnum Bar => new ABar();
        public SomeEnum Baz => new ABaz();
    }     
It's also worth noting that extension methods on C# enums give you most (all?) of the power of Java enums.


That is only equivalent within the boundaries of your API (i.e. when you use them as pure callbacks). But your delegates are two different objects, while in the Java example, it's a single object. This makes a difference if, for example, object identity matters.

From practical purpose, Java anonymous classes can be used in any scenario where you need to create a one-off object that derives from a class or implements one interface. A delegate can only be used in a scenario where the receiving variable or function wants a function type.

I'm not arguing that delegates are lambdas are bad, mind you. For the common scenario involving callbacks, they're vastly superior. But they're not a complete replacement for Java inner classes.


Yes, but we can recover all of the power of anonymous inner classes by building up a (usually small) set of "ad-hoc" classes that just wrap delegates, as I explain here: https://stackoverflow.com/questions/20703002/does-object-exp...

By the way, thanks for your work on PTVS, I just discovered it recently.


But they aren't really anonymous then, since you have to write a wrapper class for every class or interface that you intend to implement inline.

By the way, I totally forgot about F# object expressions! But they're a good example of this feature. Better than in Java, in fact, because they let you implement multiple interfaces. Also, IIRC, they're true closures (whereas Java only lets you close over finals, not writable locals).

Ideally, you'd have both those and lambdas in a language, like C# and Scala do. If I had to choose, though, I'd definitely pick lambdas - that is the 90% use case.


Brevity is the feature, not anonymity. What C# actually lacks is syntax sugar. But, a few years ago, I spent an hour or so implementing `Ad-hoc` classes for most of the interfaces and abstract classes that I thought I would ever need, and it's been sufficient over 90% of the time[1]. N.b. these classes could have been generated programmatically.

I'm not claiming that the technique is exactly equal to what Java gives you out of the box, but rather that C# can get within epsilon. In other words, for my purposes, the prefix AdHoc- might as well be a keyword (as in AdHocIEquality<T>, AdHocIEnumerable<T>†, AdHocDisposable, AdHocHttpHandler, etc...), because it's indistinguishable from syntax sugar.

On the other hand, the F# object expression really is more than just syntax sugar, because of the way it interacts with type inference (no need to upcast to satisfy the type checker), and (as you noted) that it can implement an arbitrary set of interfaces. But, it's not all carrots and apples: F# lambdas don't work well with protected members. Meanwhile, C# can close over just about anything (a ref local, such as Span<T>, being the obvious exception).

[1] https://github.com/noblethrasher/AdHoc-Implementations/blob/... (in a newer version, I implement the interfaces explicitly)

† which, really only works well in VB because lambdas can contain `Yield` statements.


Side note: in C# 7.0, local functions (which, while not anonymous, are true closures) can contain "yield".


On your delegates example for C#, since the parameters of the lambda and methods match, and you aren't relying on any closure behaviour, you can use 'method group syntax' to make it even more succinct:

  Bax.AddHandler(DoSomething, ComputeInt);


- Better enums

- The way one can plug into JIT code generation via invokedynamic, methodhandles and varhandle bytecodes

- A JIT compiler written in Java (Graal), now pluggable in the reference JVM

- An actual roadmap to rewrite remaining C++ from OpenJDK in Java (Project Metropolis)

- The research being done in Graal and Sulong about using Java to write compiler infrastructure

- Being portable to devices where the CLR probably will never be ported to

- A a better JIT in what concerns dynamic code re-writting

- Multiple implementations, some of them with AOT support, from disparate vendors

I just enjoy using both eco-systems since they exist.


> An actual roadmap to rewrite remaining C++ from OpenJDK in Java

Very cool. I wasn't aware of that. Do you think this will help introduce more "low level" features in Java since it is paramount to at least keep the same level of performance, and definitely improve it in the future?


Yes, this is nothing new, there are a few meta-circular JVMs, just they weren't commercial products.

Jalapeño from IBM,

http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.25.8...

Sun's own Squawk VM, used on Sun SPOT before IoT was a thing.

https://www.researchgate.net/publication/221320697_The_squaw...

https://en.wikipedia.org/wiki/Sun_SPOT

Regarding Project Metropolis specifically, you can find the information here.

"Your next JVM: Panama, Valhalla, Metropolis by John Rose"

https://www.youtube.com/watch?v=OMk5KoUIOy4

http://cr.openjdk.java.net/~jrose/metropolis/Metropolis-Prop...

Note that I wasn't completely fair, the .NET team also has some plans to incrementally move the runtime into C#, but so far it has only been mentioned on an InfoQ interview, no official plans revealed.

https://www.infoq.com/articles/virtual-panel-dotnet-future

"So, in my view, the primary trend is moving our existing C++ codebase to C#. It makes us so much more efficient and enables a broader set of .NET developers to reason about the base platform more easily and also contribute."


It'd be great if the secret to language design was cramming in as many features as possible.


> What are the features that c# is missing? As the other comment explained delegates are the equivalent of anonymous inner classes and are far better.

Not entirely. .NET delegates don't support generic type parameters (aka first-class polymorphism). Methods in C# and Java do.

C# interfaces also don't support static methods, which are useful for factory patterns. C#'s factory patterns require a lot more code as a result.

C# also has a bunch of artificial limitations, like not being able to specify type constraints on Delegate, Enum, and a few other built-in types. It's just nonsense.

The rest of C# is overall better than Java though.


Not sure what you mean by "delegates can't accept generic type parameters" because you can certainly make a delegate with a <T> in it in C#


There is no delegate equivalent of passing around an instance of IFoo:

    interface IFoo
    {
        void Bar<T>();
    }
This is known as first-class polymorphism.

Classes with methods are strictly more powerful than delegates, but they shouldn't be. It's even worse than that actually, because you can't even create an open instance delegate to a generic interface or abstract class method (you'll get a runtime error, not even a static error).


> The reflection API is c# is also far more useful and easy to understand.

It's really annoying that you can't query a Java package to find out what classes it defines.


A java package is a namespace right? In .NET you can't query a namespace, but you can query a module (very rarely used) or assembly. Surely Java has something to query a jar.


There's nothing built-in quite like that. However, in normal circumstances, you can do it on top of what's in the JDK: pick a class you know is in the jar, load it as a classpath resource URL, parse that URL to find the jar, then read the jar using the built-in jar support. There are other ways to do it, eg starting from the classpath and working down. There are any number of libraries for doing this, eg:

https://github.com/lukehutch/fast-classpath-scanner


A package is the equivalent of an assembly


If this is technically incorrect, as suggested by the voting, I'd appreciate someone setting me straight.


An assembly is closer to a JAR. It is a single unit which can contain multiple namespaces. However, in the Java runtime, the package is the top-level organizational unit. In .NET, it's the assembly. The fully qualified type name in .NET contains the assembly, namespace, and type name. In Java, it's just the package and type name.


Don't forget tail recursion!


I like the irony, you can not say that C# is superior to Java in every way in a discussion about C# Span while Java as ByteBuffer (the same API) since Java 4 (circa 2002 i think).


Span<T> out of the box you can use with any type, not just Byte, Char, Double, Float, Int, Long, Short

It can be put over an array (as per ByteBuffer); but also over stack allocated memory; a native pointer T* etc

The underlying allocation is separate from the type


ByteBuffer provides universal access to on heap/off heap data.

JNI or Unsafe allow to create ByteBuffer from native pointers, created in C/C++ or in Java using malloc, memcopy them, etc. This is used (and abused) by most web servers, DataStax or LMAX-Exchange have even created their whole business on that.

You also have have CharBuffer, DoubleBuffer, FloatBuffer, etc

There is no stack allocated ByteBuffer in Java (Java provides no stack access, this is religious) but in Java can do relaxed data access on Buffer element, volatile access, opaque access, CAS, etc.

This is the same API.


Does it work over non-primitive and user defined types?

Object, InetAddress, etc

Though only `struct` value types should be used in stack or native memory due to risk of GC holes


No, not yet. I believe there is a prototype of that kind of buffers that comes with the valhalla project (value types) but i've not tested it.




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: