On Friday, 11 October 2019 at 17:50:42 UTC, Jonathan M Davis
wrote:
Generic functions and types operate on Object underneath the
hood. If you have Container and Container, you really
just have Container with some syntactic niceties to
avoid explicit casts. You get type checks to ensure that
Container isn't given a Bar unless Bar is derived from
Foo, and the casts to and from Object when giving
Container a Foo are taken care of for you, but it's still
always Container underneath the hood.
In the case of Java, the type of T in Container or foo()
is truly only a compile time thing, so the bytecode only has
Container and no clue what type is actually supposed to
be used (the casts are there where the container or function is
used, but the container or function has no clue what the type
is; it just sees Object). That makes it possible to cheat with
reflection and put something not derived from Foo in
Container but will then usually result in runtime failures
when the casts the compiler inserted are run. C# doesn't have
that kind of type erasure in that the information that
Container contains Foo rather than Object is maintained at
runtime, but you still have a Container. It's just a
Container with some metadata which keeps track of the
fact that for this particular object of Container,
Object is always supposed to be a Foo. As I'm a lot less
familiar with C# than Java, I'm not all that familiar with what
the practical benefits that gives are, though I'd expect that
it would mean that reflection code would catch when you're
trying to put a Bar into Container and wouldn't let you.
Note that for generics to work, they have to a common base
type, and you only ever get one version of a generic class or
function even if it gets used with many different types derived
from Object. For a primitive type like int or float (as well as
for structs in the case of C#), they have to be put into a type
derived from Object in order to be used with generics (as I
expect you're aware, C# calls this boxing and unboxing).
Templates don't act like this at all.
Unlike Java, C# actually does generate different code pieces for
different value types [1] and reuses the same generated code for
reference types.
[1]
https://alexandrnikitin.github.io/blog/dotnet-generics-under-the-hood/