|
|
|
Advertisement
|
Bruce Eckel: How do constraints work in C# generics?
Anders Hejlsberg: In C# generics, we have the ability to put constraints on type
parameters. Take for example our List<T>. You could then say,
class List<T> where T: IComparable. And that means T
must implement IComparable.
Bruce Eckel: Interesting. In C++, the constraints are implied.
Anders Hejlsberg: Yes. And in C#, you can of course do the same thing. Say we have a
Dictionary<K, V> that has an add method that takes K
key and V value. The add method implementation will
likely want to compare the passed key to the keys already in the Dictionary,
and it might want to do that using an interface called IComparable. One way it
could do this is cast the key parameter to IComparable and
then invoke the compareTo method. And of course, when you do that you
have created an implicit constraint on the K type and on the
key parameter. If the passed key doesn't implement
IComparable, you get a runtime error. But it's nowhere stated really in any of
your methods or your contracts that key must implement
IComparable. And of course you have to pay the overhead of runtime type
tests, because you're actually doing a dynamic type check at runtime.
With a constraint, you can hoist that dynamic check out of your code and have it be
verifiable at compile time or load time. When you say K must implement
IComparable, a couple of things happen. On any value of type
K, you can now directly access the interface methods without a cast, because
semantically in the program it's guaranteed that it will implement that interface. Whenever
you try and create an instantiation of that type, the compiler will check that any type you
give as the K argument implements IComparable, or else you
get a compile time error. Or if you're doing it with reflection you get an exception.
Bruce Eckel: You said the compiler and the runtime.
Anders Hejlsberg: The compiler checks it, but you could also be doing it at runtime with reflection, and then the system checks it. As I said before, anything you can do at compile time, you can also do at runtime with reflection.
Bruce Eckel: Will I be able to do a template function, in other words, a function where the argument is the unknown type?
You are adding stronger type checking to the containers, but can I also get a kind of weaker typing as I can get with
C++ templates? For example, will I be able to write a function
that takes parameters A a and B b, and then in the code say, a + b?
Can I say that I don't care what A and B so long as there's an operator+ for them,
because that's kind of a weak typing.
Anders Hejlsberg: What you are really asking is, what can you say in terms of constraints?
Constraints, like any other feature, can become arbitrarily complex if taken
to their ultimate extreme. When you think about it, constraints are a pattern matching
mechanism. You want to be able to say, "This type parameter must have a constructor that
takes two arguments, implement operator+, have this static method,
has these two instance methods, etc." The question is, how complicated do you want this pattern
matching mechanism to be?
There's a whole continuum from nothing to grand pattern matching. We think it's too little to say
nothing, and the grand pattern matching becomes very complicated, so we're in-
between. We allow you to specify a constraint that can be one class, zero or more interfaces,
and something called a constructor constraint. You can say, for example, "This type must implement
IFoo and IBar," or "This type must inherit from base class
X." Once you do that, we type check everywhere, at both compile and run
time, that the constraint is true. Any methods implied by that constraint are directly
available on values of that type parameter type.
Now, in C#, operators are static members. So, an operator can never be a member of an
interface, and therefore an interface constraint could never endow you with an
operator+. The only way you can endow yourself with an
operator+ is by having a class constraint that says you must inherit from, say,
Number, and Number has an operator+ of two
Numbers. But you could not in the abstract say, "Must have an
operator+," and then we polymorphically resolve what that means.
Bill Venners: You did the constraints by type, not by signature.
Anders Hejlsberg: Yes.
Bill Venners: So the type must either extend a class or implement interfaces.
Anders Hejlsberg: Yes. And we could have gone further. We did give thought to going
further, but it gets very complicated. And it's not clear that the added complexity is worth
the small yield that you get. If something you want to do is not directly supported in the
constraint system, you can do it with a factory pattern. You could have a
Matrix<T>, for example, and in that Matrix you would
like to define a dot product method. That of course that means you ultimately need to
understand how to multiply two Ts, but you can't say that as a constraint, at
least not if T is int, double, or
float. But what you could do is have your Matrix take as an
argument a Calculator<T>, and in Calculator<T>,
have a method called multiply. You go implement that and you pass it to
the Matrix.
Bruce Eckel: And Calculator is a parameterized type also.
Anders Hejlsberg: Yes. It is sort of a factory pattern. So there are ways of doing these things. It's maybe not quite as nice as you'd like, but everything comes at a price.
Bruce Eckel: Well, yes, I started seeing C++ templates as sort of a weak typing mechanism. And when you start adding constraints on top of that, you're going from the weak typing to the strong typing. It always gets more complex when you add strong typing. It's a spectrum.
Anders Hejlsberg: The thing you realize about typing is that it's a dial. The higher you
place the dial, the more painful the programmer's life becomes, but the safer it becomes too.
But you can turn that dial too far in either direction.
Come back Monday, February 2 (Ground Hog's Day!) for the final installment of this conversation with C#'s creator Anders Hejlsberg. If you'd like to receive a brief weekly email announcing new articles at Artima.com, please subscribe to the Artima Newsletter.
Have an opinion about the design principles presented in this article?
Discuss this article in the Articles Forum topic,
Generics in C#, Java, and C++.
Resources
Deep Inside C#: An Interview with Microsoft Chief Architect Anders Hejlsberg:
http://windows.oreilly.com/news/hejlsberg_0800.html
A Comparative Overview of C#:
http://genamics.com/developer/csharp_comparative.htm
Microsoft Visual C#:
http://msdn.microsoft.com/vcsharp/
Dan Fernandez's Weblog:
http://blogs.gotdotnet.com/danielfe/
Eric Gunnerson's Weblog:
http://blogs.gotdotnet.com/ericgu/
|
Sponsored Links
|