Generics and C#
Osborn: Maybe we should talk a little bit about version 2.0. Certainly, [C# programmers have been] looking forward to generics for a long time.
Osborn: What's different about the generics in C# as opposed to other languages?
Hejlsberg: I think that begs the question of what's different between C# and Java, obviously.
Hejlsberg: First of all, I'm very pleased that we got generics into 2.0. Generics in many ways is the deep enabler of all the stuff that you're now seeing us do in C# 3.0. It is really profound how generics adds a new dimension of richness to the type system that opens up all sorts of possibilities, like language-integrated query, which we could've never done without generics. So in that sense, it's a deep enabler for interesting stuff. It also is a very pragmatic real-world problem solver.
Hey, more typing is good, because that means you find more errors sooner, and you can do better code generation because you have to have fewer dynamic checks at runtime to verify the solidity of the types in your program.
Now, with respect to Java and C#, syntactically, the two implementations of generics look very similar. They [both] look kind of like C++ templates; you can see the heritage there.
But once you scratch the surface, underneath they're actually very, very different. I think the biggest difference is that in .NET, generics are not just a language feature. They are deeply understood by the CLR [common language runtime] and by the type system of .NET itself. So [generics] have representation at runtime.
Java chose a different strategy for implementing generics, where in a nutshell, they're only a compile time feature. And the [Java] compiler removes all of the genericity from the code, and just emits objects; it effectively substitutes
object for every type parameter. With Java, at runtime, there are no generics. This is interesting in the sense that it allows you to run on an unmodified VM [virtual machine], but it also brings about a host of very surprising limitations and rules that you have to abide by. And it does not give you some of the performance gains that we see from [our own implementation of] generics, because [with Java] at runtime, there are no generics and you have to still do all of the dynamic runtime checks and downcasts and whatever while you take things out of a
But I think the subtler point here is that because there is no runtime representation of generics [in Java], you lose some information going from your compiled code to the code that you run at runtime. So at compile time, you might be operating on a list of customers. If at runtime, someone hands you a list of customers typed as
object, they just give it to you as an object, and [if ] you want to find out what this list is a list of, you can't, because reflection doesn't know about generics because that got erased.
And so you have these strange holes in the system, and in a world where we rely increasingly on dynamic code generation and inspection--of running code and dynamic behaviors and whatever--that is actually, to me, probably the biggest issue that I have with Java's implementation of generics; that is, this lack of true representation of the program being run.
Osborn: So you're saying that the [.NET implementation of] generics allows you to hang onto the--
Hejlsberg: Oh, certainly. If I give you a
List<T> typed as
object, I can ask it, "What are you?" And it will say, "I am a list of customers." It'll say, "I'm a
Customer." Then I can say, "Well, why don't you give me just the
List<T>? And in fact, why don't you bind
Order?" And now I can make myself a
List<Order>, and then I can create instances on that. Anything I can do at compile time, I can do at runtime too, through reflection, and that's tremendously powerful.