Sponsored Link •
Anders Hejlsberg, the lead C# architect, talks with Bruce Eckel and Bill Venners about delegates and C#'s first class treatment of component concepts.
Anders Hejlsberg, a distinguished engineer at Microsoft, led the team that designed the C# (pronounced C Sharp) programming language. Hejlsberg first vaulted onto the software world stage in the early eighties by creating a Pascal compiler for MS-DOS and CP/M. A very young company called Borland soon hired Hejlsberg and bought his compiler, which was thereafter marketed as Turbo Pascal. At Borland, Hejlsberg continued to develop Turbo Pascal and eventually led the team that designed Turbo Pascal's replacement: Delphi. In 1996, after 13 years with Borland, Hejlsberg joined Microsoft, where he initially worked as an architect of Visual J++ and the Windows Foundation Classes (WFC). Subsequently, Hejlsberg was chief designer of C# and a key participant in the creation of the .NET framework. Currently, Anders Hejlsberg leads the continued development of the C# programming language.
On July 30, 2003, Bruce Eckel, author of Thinking in C++ and Thinking in Java, and Bill Venners, editor-in-chief of Artima.com, met with Anders Hejlsberg in his office at Microsoft in Redmond, Washington. In this interview, which will be published in multiple installments on Artima.com and on an audio CD-ROM to be released this fall by Bruce Eckel, Anders Hejlsberg discusses many design choices of the C# language and the .NET framework.
Bill Venners: One way C# differs from Java is in how it propagates events to interested objects. Java uses classes, often inner classes, that implement listener interfaces. C# uses delegates, which are a bit more like function pointers. Why delegates?
Anders Hejlsberg: Let me first talk a little bit about how I view simplicity in general. No one ever argues that simplicity isn't good, but people define simplicity in a variety of ways. There's one kind of simplicity that I like to call simplexity. When you take something incredibly complex and try to wrap it in something simpler, you often just shroud the complexity. You don't actually design a truly simple system. And in some ways you make it even more complex, because now the user has to understand what was omitted that they might sometimes need. That's simplexity. So to me, simplicity has to be true, in the sense that the further down you go the simpler it gets. It shouldn't get more complicated as you delve down.
Anders Hejlsberg: Delegates add a kind of expressiveness that you don't get with classes or interfaces, which is why I think they are important. Programming languages that have gone before us have recognized that they are important. They have many names: function pointers, member function pointers. In LISP they are closures. They are what functional programming is all about when it comes right down to it. They are tremendously useful.
Bill Venners: In what way?
Anders Hejlsberg: You can indeed do with interfaces what you do with delegates, but you are often forced to do a lot of housekeeping. Let's look at how you handle events in Java, for example, versus how you handle events in the .NET world. Because there are no delegates in Java, you end up using interfaces.
You define an interface that represents all of your events. The interface could declare one, two, three, four methods or more. Already, there's an issue. It's not particularly clear how to structure that. How many interfaces do you have for your outgoing events? Do you have one interface per event or one interface for all events? There's no clear guidance, and sometimes it falls somewhere in-between. So you fudge that one out. Now, to handle events coming out of the component, you have to implement the interface. Of course if you want to handle the same set of events from two different components, then you have to implement the interface twice, which you cannot do. In that case you need to create an adapter. This is where the housekeeping starts getting in your face.
Inner classes can help a bit with that housekeeping, but nonetheless, part of the problem with using interfaces is that the receiver of the event has to know that he is receiving the event. He has to explicitly implement the listener interface. With a delegate, by contrast, as long as the signatures are compatible, you can just slot them together. The guy handling the event doesn't care how he got called. It's just a method.
Bruce Eckel: It's sort of a weaker typing.
Anders Hejlsberg: Yeah, indeed it is.
Bruce Eckel: So it's more flexible.
Anders Hejlsberg: Yes, exactly. It's purely based on whether you have compatible signatures, i.e., parameter lists. If you do, you can wire them together. And it also conceptually completely matches the end user expectation of what happens with a call back, right? Give me some parameters and I'll write some code. Sure sounds like a method to me. Sorry, I'd like to give you a reference to that method, and that's what a delegate is.
Bruce Eckel: And yet in the end you don't lose the type checking. The type checking is happening at runtime?
Anders Hejlsberg: No, even more is happening at compile time. When you construct a delegate, you get what a C++ programmer might call a bound member function pointer. The delegate references a method of a particular object. If that method is virtual, you can actually figure out precisely which one it is going to be. So in a sense, you can resolve the virtualness at delegate construction time. The call through the delegate is literally just an indirect call instruction.
Bruce Eckel: It doesn't have the extra indirection.
Anders Hejlsberg: That's right, you can resolve the VTBL indirection once and for all when you construct the delegate, and then with every call through the delegate you're just going straight to the method. So not only can delegates be more efficient than interface dispatch, they can be more efficient than regular method dispatch.
Bruce Eckel: C# also has a multicast delegate, in which one delegate causes multiple functions to be invoked. Is that an orthogonal feature?
Anders Hejlsberg: Multicast is a completely orthogonal feature. And honestly, I am neutral on whether multicast is a great thing. I think it has its uses, but I also think we could defensively have said all delegates are single cast. Some people find multicast very important, and there are some very good uses for it, but in the vast majority of cases, delegates are just single cast. Indeed we've constructed the system such that you don't actually pay for the multicast feature unless you use it.
Bill Venners: How do delegates fit in with what you were saying before about simplicity and simplexity? Where is the simplicity and where is the simplexity?
Anders Hejlsberg: If you emulate delegates with interfaces, you end up with all this housekeeping and adapters. Indeed, look at any tool that wires together JavaBeans. They generate these adapters and say, "Don't modify this code down here. We're going to spit out these reams of funky little helper classes for you." That to me is simplexity. The system is not truly simple. The system is actually quite complex, but if you squint it appears to be simple.
Bill Venners: In an interview published on the O'Reilly Network, you justified C#'s support for properties and events by saying, "Developers are building software components these days. They are not building monolithic applications or monolithic class libraries. Everybody is building components that inherit from some base component provided by some hosting environment. These components override some methods and properties, and they handle some events, and put the components back in. It's key to have those concepts be first class."
I want to understand better what you mean by that, because I tend to think of myself as building classes, not components. Are you talking about people who are building components for other people to connect together in a Delphi, Visual Basic, or JavaBeans way? Is that what you mean by component?
Anders Hejlsberg: The great thing about the word component is that you can just sling it about, and it sounds great, but we all think about it differently. In the simplest form, by component I just mean a class plus stuff. A component is a self-contained unit of software that isn't just code and data. It is a class that exposes itself through properties, methods, and events. It is a class that has additional attributes associated with it, in the form of metadata or naming patterns or whatever. The attributes provide dynamic additional information about how the component slots into a particular hosting environment, how it persists itself—all these additional things you want to say with metadata. The metadata enables IDEs to intelligently reason about what a component does and show you its documentation. A component wraps all of that up.
Bill Venners: What I think of when I'm using Java is that I'm designing
class libraries, not component libraries, and maybe that is because
set is clunky. I do use
set, and I certainly fire events, but I don't
intend for these classes to be used in a Bean builder IDE. I am imagining that
they'll be used by people who write code. So I'm still left wondering how many
people are actually writing JavaBean-like components, and if that's the trend,
because I don't see it much in my own experience.
Anders Hejlsberg: The object-oriented programming languages in
mainstream use today are all hybrids. They have a lot of structured
programming in them. And objects are still in many ways just structs with a
bunch of methods and a
this pointer. I think that conceptually,
when you think of an object or a component, they do have properties and they
do have events. And giving first class treatment to those things in the
programming language makes that easier.
People will argue that C#'s support for properties and events is just syntactic
sugar, but it's all just syntactic sugar, right? Heck, objects are just syntactic
sugar. We could just all roll our own VTBLs, which you could do with C
macros, right? Sure, you could write object-oriented programs in C. It's just a
hell of a lot more complicated. And likewise, you can write components in C++
or Java, but because the core concepts are not first class in those
programming languages, you end up having to squint more. There's that word
again. With properties, they're not really properties, they're a
getBla and a
setBla. But when they show up in a
property inspector, they're a
bla. And you just have to know that
mapping is there.
It is clear that components are a trend. It is the way we are all using our classes, but in most of the languages in which we deal with components, they are not first class concepts. And I'm just saying that they should be. We have been talking about the PME programming model—properties, methods, and events—for a long time now, and we've all used that in our day-to-day programming. Why don't we actually give it first class treatment in the programming language?
Come back Monday, September 8 for part VIII of a conversation with Elliotte Rusty Harold about the design of the XOM API. I am now staggering the publication of several interviews at once, to give the reader variety. The next installment of this interview with Anders Hejlsberg will appear on Monday, September 15. If you'd like to receive a brief weekly email announcing new articles at Artima.com, please subscribe to the Artima Newsletter.
Deep Inside C#: An Interview with Microsoft Chief Architect Anders Hejlsberg:
A Comparative Overview of C#:
Microsoft Visual C#:
Anders Hejlsberg was not the first Artima interviewee to mention taste. Jim Waldo made almost
an identical comment about building a team of tasteful programmers in his interview:
And an entire portion of Ken Arnold's interview was devoted to design taste - Taste and Aesthetics: