Monthly Archives: March 2004

TryCast (aka. the ‘as’ operator)

About 8 months ago, I promised to explain why the C# ‘as’ operator was worth adding to VB and then never got back to it. While trying to figure out what comments I missed, I came across the promise again and figured I’d take the opportunity to rectify the situation.

In Whidbey, we’re going to introduce YACO (Yet Another Conversion Operator) called TryCast that’s equivalent to the C# ‘as’ operator. Why? It turns out that it’s useful in some specific, but not uncommon, situations. Let’s say that you want to write a method that accepts an object and, if it implements a particular interface, do something with it. For example:

Sub Print(ByVal o As Object)
Dim PrintableObject As IPrintable

If TypeOf o Is IPrintable Then
PrintableObject = DirectCast(o, IPrintable)
PrintableObject.Print()
End If
...
End Sub

Ok, great, this works fine. The problem is, though, that we’re doing redundant work here. You see, when the CLR executes the If statement, it does a type check for the TypeOf expression. If o does implement IPrintable, it then goes ahead and casts o to IPrintable, which does another type check to ensure that the value really does implement IPrintable. So you’re doing two type checks, and type checks can be expensive. (When I say “expensive” here, I don’t mean expensive like “$760,000 for a Ferrari” expensive, I mean more like “$3.50 for a latte twice a day adds up over 365 days” expensive.)

What TryCast does is it allows you to combine the two type checks into one. TryCast, as its name suggests, will try the cast and, if it succeeds, return the value cast to that type. Otherwise, it returns the value Nothing. So you can rewrite the code above as:

Sub Print(ByVal o As Object)
Dim PrintableObject As IPrintable = TryCast(o, IPrintable)

If PrintableObject IsNot Nothing Then
PrintableObject.Print()
End If
...
End Sub

Voila, two type checks becomes one! Even if we hadn’t gotten any feature requests for this, we were planning to do it anyway because we found that several of our language helpers that deal with object values could be sped up ~5% just by eliminating the redundant type checks. So it’s not nothing. (So to speak.)

One question that might come to mind is: why not just optimize the first code into the second code under the covers instead of introducing a new operator? We did consider this at the compiler level, and the CLR could also choose to do it at the JIT level. However, while we could optimize this particular case, it’s easy to construct cases that would be difficult for the compiler or JIT to optimize. Which would mean that you could make a minor tweak to the structure of your code and suddenly lose the optimization without knowing it. In this case we figured explicitness was better…

Update 4/3/04: Made some corrections based on comments below.

Blog software is only as good as its developer…

…and in this case, since I’m the developer, what does that say about me? I just discovered that when I wrote the code to produce the comments feed for my blog, I screwed up. Rather than giving the past x days of comments, my comments RSS feed was giving all the comments for the past x days of entries. So if you commented on an entry that was more than x days (I think it’s something like 10 or 14) old, then it wouldn’t ever show up in the feed. I noticed this by accident when cross-linking to my old IsNot post, and now I think I’ve got it fixed.

So if you’ve submitted some comments and I totally ignored you, it might be because I was relying on a broken comments feed. Then again, it could be because I’m a lazy bastard… You’ll just have to guess!

(I will be going back through all my comments to find ones that I might have missed…)

IsNot and the fall of civilization

Back when I said that Whidbey is going to be introducing an IsNot operator, Karl raised some objections to the operator in the comments, to which I said:

Karl, it seems like your question isn’t so much why we’re adding “IsNot” but why we have “Is” in the first place. It’s a good question, I’ll try to address it in an entry soon.

…and then I never came back to it. Now that Daniel has picked up the thread again, let me return to the subject and elaborate just a little bit.

As I said in the comments, the question that Karl and Daniel raise is not so much “why IsNot?” but instead “why Is?” The fundamental reason for adding IsNot was that it’s irritating to have a comparison operator and not its inverse. For example, it’s entirely feasible to have a language with just Not, =, < and <=, but no language bothers to be “pure” that way because it’s a PITA to have to write “If Not x = 5 Then” or “If Not x <= 5 Then” and so on. Given the existence of the “Is” operator, having its opposite seems like, as Martha would say, “a good thing.” Charges of language pollution seem, on the face of it, to be a stretch.

OK, so let’s leave aside the question of IsNot for a moment. Why bother to have two comparison operators, “Is” and “=“? There’s a historical reason and a modern reason. The historical reason is that, prior to VS 2002, VB allowed classes to define a parameterless default property that represented the “value” of the class. This feature allowed, say, a TextBox object to declare that its Text property was its “value” property and then the user could write “TextBox1 = “foo”” and it would assign the string value “foo” to TextBox1.Text. However, since a class could be both an object and a value, you needed to have two forms of comparison and assignment to distinguish between the two. Assignment was handled by splitting it into “Let” vs “Set” assignment: “Let TextBox1 = “foo”” assigned a value to the default property, while “Set TextBox1 = New TextBox()” assigned a value to TextBox1 itself. Comparison was handled by splitting it into, yes, you guessed it, “=” and “Is”. The code “TextBox1 = “foo”” would do a value comparison between the string value “foo” and the default property TextBox1.Text. The code “TextBox1 Is TextBox2”, on the other hand, would do an object comparison between two TextBox objects.

When we made the leap to .NET, though, we ran up against a bit of a wall. Other languages such as C# and C++ didn’t support the concept of parameterless default properties, two types of assignment, two types of comparison, etc. This wasn’t a big deal for fields, but it was a huge issue for properties – whereas a VB property could define a Get, Let and Set, a C# property could define only a Get and a Set. We spent a lot of time trying to invent ways to map between the two schemes and finally came to the conclusion there was no good mapping. (Even today this is one of the friction points between COM and .NET that can make interop a pain.) So we gave in and dropped parameterless default properties from the language. This meant that we could also drop Let vs. Set assignment from the language. And, if we’d chosen too, we could have dropped “Is”. But we didn’t…. Why is that? Two reasons, one minor, one major.

The minor reason is that we could produce more optimal comparisons between values typed as Object (if someone’s really interested in this, I can explain, but it’s kind of obscure). The major reason was that we were anticipating a feature that didn’t make it into VS 2002 or VS 2003 but will make it into Whidbey: operator overloading. Take the situation where you’ve got a class that overloads the equality operator:

Class ComplexNumber
...

Public Shared Operator = (ByVal c1 As ComplexNumber, ByVal c2 As ComplexNumber) As Integer
...
End Operator
End Class

Now, when you say “c1 = c2“, you’re always going to get value equality between two ComplexNumber instances, not reference equality. But let’s say that you really need to know whether c1 and c2 are the same instance not just the same value. How do you do it? In VB, it’s simple. You say “If c1 Is c2 Then…“. What do you have to do in C#? You have to make sure you cast both values to object and then do the comparison: “if ((object)c1 == (object)c2) {…}“. Forget the cast, and you’ve accidentally slipped back into value comparison.

So that’s why we kept “Is“ and “=“. And, yes, I think this is one of the places where our syntax is clearer than C#’s. Although everyone’s free to disagree…