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<Foo> and Container<Bar>, you really just have Container<Object> with some syntactic niceties to avoid explicit casts. You get type checks to ensure that Container<Foo> isn't given a Bar unless Bar is derived from Foo, and the casts to and from Object when giving Container<Foo> a Foo are taken care of for you, but it's still always Container<Object> underneath the hood.

In the case of Java, the type of T in Container<T> or foo<T>() is truly only a compile time thing, so the bytecode only has Container<Object> 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<Foo> 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<Foo> contains Foo rather than Object is maintained at runtime, but you still have a Container<Object>. It's just a Container<Object> with some metadata which keeps track of the fact that for this particular object of Container<Object>, 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<Foo> 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/

Reply via email to