I'm curious: What are the breakages in backwards compatibility that C# introduced?
The only one I can think of is the upcoming change in closure semantics for loop variables - something that is being changed to make it work like people would expect for 99% of the case and risks breaking that 1% of cases where someone actually wants the same loop variable to be closed over in all functions defined within the loop.
(e.g. Linq is actually much like a monad with SelectMany as bind)
Correct me if I'm wrong but I believe, with SelectMany, Linq does in fact, provide everything required to support monads.
Agree with everything else you said - especially on flawed comparisons between the two languages.
The biggest breaking change was from version 1 to 2. Generics were added, as were partial classes, private setters on properties, and delegates. Along the way a lot of little breaking changes were also taken on. Things that change the behavior of the language enough to warrant calling out the incompatibility from previous versions (if you happened to rely on a particular behavior) but which generally result in an improvement in the language.
Not easily. There are a few rare instances where they could cause an error during compilation of code that compiled fine for .net 1.1, and some problems with using tlbimp. I don't think it caused previously compiled code to break though.
I don't think there was much backwards compatibility breaks, but in generics C# broke forward compatibility of the bytecode, whereas Java didn't (they used type erasure for their generics).
With that said, C# almost always breaks forward compatibility. It seems like a somewhat odd design goal to have for a language nowadays.
I'm sure you probably know this but that kind of breaking change (to the degree that we can agree that it even exists) is not a C# breaking change since generics aren't a purely-language-feature the way they are in Java. I don't have the C# spec handy but I'm pretty sure that it doesnt prescribe that generics are reified.
I'm not actually intimately familiar with C#. I remember reading about some of the changes between major .NET version (e.g. 2 to 3) which did not maintain backwards compatibility. However, on review, that may have been backwards compatibility of the bytecode rather than the language proper, in which case I am mistaken.
It would be great if somebody who knows one way or the other for sure chimed in.
C#3 adds features not found in C#2 (just as C#2 added features not found in C#1).
However the C# compiler will produce bytecode compatible with the .NET 2.0 runtime from C#3-code. And all C#2 code will compile just fine with the C#3 compiler.
Basically what breaks is trying to shove C#3 code through a C# 2 compiler, and that can hardly be said to surprise anyone.
The part which may be confusing is that unlike Java and the JVM, C# is tied to CLR runtimes, and you can have several installed at the same time.
C#1 was tied to the .NET 1.x runtime. C#2 was tied to the .NET 2.x runtime (which also powered .NET 3.0 and .NET 3.5). C#3 was tied to the .NET 2.x runtime with some extra LInQ DLLs installed (aka .NET 3.5). C#4 (which again adds more features) depends on the .NET 4.x runtime. And all these runtimes can be installed at the same time.
The fact that Microsoft is willing to "leave old runtimes behind" and create new ones when the language features being requested requires it means they can iterate much faster than the Java can, since backwards bytecode compatibility is not required.
I think it is a decision which has worked out largely to their advantage.
The breaking changes are usually minor and side-effects of the other language changes. For example, IEnumerable<T> was made covariant in .Net 4.0. Consider what happened to code passing an IEnumerable<List<int>> to a method with two overloads taking IEnumerable<Object> and Object respectively.
The only major breakage has not been in the language, but in the .NET framework libraries; and then mostly in ASP.NET. Between 1.1 and 2.0, quite a few things broke there. Upgrading was no fun at all.
The only one I can think of is the upcoming change in closure semantics for loop variables - something that is being changed to make it work like people would expect for 99% of the case and risks breaking that 1% of cases where someone actually wants the same loop variable to be closed over in all functions defined within the loop.
(e.g. Linq is actually much like a monad with SelectMany as bind)
Correct me if I'm wrong but I believe, with SelectMany, Linq does in fact, provide everything required to support monads.
Agree with everything else you said - especially on flawed comparisons between the two languages.