Friday, November 09, 2007 8:28 AM
Designing for Intellisense
Atalasoft was at DevConnections this week, and I went to a some of the sessions including one on a deep dive of Linq, where I learned why Linq looks like this:
from ord in orders select ord.id, ord.amount
Instead of this:
select ord.id, ord.amount from ord in orders
The first example enables intellisense in the IDE to help you once you are finished with the from clause. I have to say, that I was a little shocked when he first said it. Up until then, I had filed this difference with SQL in the "Microsoft just has to be a little different" part of my brain that also knows when to use backslashes and where the menu bar is.
The other thing that shocked me, is that I had never heard of a language feature that was specifically designed to be IDE-friendly. Certainly, there are features in languages that turn out that way. Having curly-braces helps Visual Studio reformat your program, but C# doesn't have curly-braces for that reason (or maybe it does -- did K&R put curlies into C because of ed -- beats me). In any case, if there's a language feature that exists just for the editor, I hadn't thought of it that way.
It kind of got me thinking about method-calling syntax, which usually is
object.method( ... )
but, in Ada and some languages it's
Which, until now, seemed to me to be an arbitrary choice, but with advanced editors, the first can actually be quite helpful. I guess the second would be good if I were more familiar with operations I could do and wanted the IDE to tell me which objects I could do them to, but that doesn't seem like the usual case to me.
The idea of language features being designed for the IDE leads to the question of whether API's should be designed for the IDE as well. This comes up in DotImage when dealing with images and commands. In DotImage, we have an AtalaImage object that represents an image and an ImageCommand base class that image processing classes derive from. You use commands like this:
AtalaImage img = new AtalaImage("image.jpg");
ImageCommand cmd = new CropCommand(cropRect);
ImageResults res = cmd.Apply(img);
But, if you're new to our API, and you're wondering how to crop an image, you might want to type "img." and let the IDE pop up all of the choices. If you did, Crop would not be there. This makes sense, because we have over 150 commands, and we would not want to pollute AtalaImage with all of them. Also, if we made them methods, it would not be easy for our users to extend the API with their own commands that behave just like ours, and they wouldn't be able to inherit from the ones we have already. There are other reasons, like undo support and other features that operate on commands, but you get the point -- commands themselves are interesting enough to be classes, not just methods on an image.
C# 3.0 adds another feature for just that situation, Extension Methods, and now I'm starting to think that they exist just for intellisense too. The basic idea is that you make static functions callable via dot syntax on an object if you make the first parameter the class of the object and mark it with "this". Continuing this example, a Crop method could be added to AtalaImage with this code:
public static class AtalaImageCommands
public static ImageResults Crop(this AtalaImage img, Rectangle r)
return new CropCommand(r).Apply(img);
Now, I can call
img.Crop(...) as long as the namespace with this class is imported, and the IDE will show Crop as a method as well. The other nice thing is that anyone extending the library can add extension methods too (so their commands would work like the built-in ones).
I'm not saying that we'll definitely use this (150 methods would still show up in the IDE), but choosing the 10 most used ones might be a good idea. Also, since you can control it by putting the extension methods in different namespaces, you can let the programmer decide when they want them to show up. We could put extension methods for commonly used commands in the namespaces with the command classes, and put the rest in a sub-namespace.