But their recommended workaround for writing generic containers is to use the empty interface and type-cast in and out --- which has all the drawbacks of the Java implicit boxing. And on top of that, it clutters up the code with type casts, adding injury to insult because the type-cast clutter actually costs you type safety. So, this has all the disadvantages of the Java approach and none of the advantages. Win?
At compile, there's one version of a generic method (say List<T>.Add). Compilation isn't slowed down like in C++.
At runtime, a generic method is compiled (on demand) into unique versions for each T encountered if T is a ValueType (int, double, etc. including user defined ValueTypes). This avoids boxing, like in Java.
Also at runtime, for all T's that are reference types a single common version is used with implicit casting as in Java.
C#'s draw back is that there is a small cost incurred on the first use of a generic method, and then an additional cost for each new ValueType used (if any). In practice this doesn't ever seem to be an issue, but it's there.
That sounds like a variation on rsc's 3rd approach (but with runtime code generation instead of boxing) with the same drawback: slower execution. It also makes certain kinds of debugging harder (how do you map a PC that's pointing to generated code? what if the code generation is the thing going wrong?).
C# on CLR is a VM approach, whereas Go compiles to native code. Concepts that are easy in the first (e.g. runtime code generation) often don't map particularly nicely to the second.
Why would you expect execution to be slower? If you invoke List<T>.Add(T) where T = int you end up running the same machine code that would have been generated if instead you had a method List.Add(int). There is a cost paid to generated that specific method, but it's on the first invocation. I'd expect it to be much faster than boxing, because you can elide a bunch of a allocations and generate tighter code since you have additional typing information at code gen time.
I suppose debugging could go sideways in theory, but it doesn't happen in practice on .NET and I doubt the go team couldn't figure out how to make it work.
It's worth noting that .NET never interprets bytecode, it always converts a method into machine code before execution. Go has already had dynamic code generation as part of it's implementation (I don't know if it's still around in 1.2), so it's hardly like it can't do the same thing .NET is.
Okay, then how does AOT works with C# templates? Really how? Do you have to pre-declare some known types ahead? (Or could it be that the AOT compiler scans all possible variants (doable since it's static language))?