Monday, October 17, 2005

 

Hiding details you can't hide

In a previous post, I mentioned the perils of abstraction, and how we can safely abstract away what we know, not what we don't know.
Unfortunately, language designers often want to hide to the ignorant programmers (that is, the rest of us) some "details" that they deem to hard to understand or master, or too boring to deal with continuously, and so on. They are often well-intentioned (although a little patronizing :-), but the result is often a huge mess.
Consider COM: it started as an easy way to write language-independent (sort of) macro-components in C++. Initially, there was no notion of a threading model: you should have been careful, on the producer and consumer side. That was it.
When VB came along with its support for COM, they decided that the programmer was too stupid to master concurrency (not to mention the thread affinity of old ODBC drivers and the like) and they invented the threading models. That was a well-intentioned bad idea :-), because even today, just a minority of programmers really understand the subtleties of COM threading models ( quick check: can you really explain the difference between choosing "free threading" or "both" in the ATL wizard? :-).
Now, when C# and .NET came along, someone must have decided that exposing the difference between value types and reference types all the time would have been bad. Indeed, I like the idea of implicit boxing, as explicit boxing a-la Java is a pain in the @$$. However, they also thought it would have been smart to tell people that structures (value types) are not that different from classes (reference types), because both can implement interfaces. Of course, this is not true. There is a run-time boxing when you cast a structure to an interface, but of course, that detail can be hidden. Or maybe not. One of my customers found himself stuck on a code that, largely simplified, could look like this:

interface M
{
void Set( int x ) ;
}

struct S : M
{
public S( int x ) { v = x ; }
public int Get() { return v ; }
public void Set( int x ) { v = x ; }
private int v ;
}

class Test
{
public static void Change( M m )
{
m.Set( 24 ) ;
}
static void Main()
{
S s = new S( 23 ) ;
Change( s ) ;
System.Console.WriteLine( s.Get().ToString() ) ;
}
}

In the real code, Change() was not static, and it was called sometimes passing instances of classes, and sometimes instances of structures. The structure was written before the need for implementing the interface was discovered. But isn't that what inheritance and polymorphism is about? :-)
Of course, the code writes "23", not "24", because is a boxed structure that gets modified, not the original structure, that is not implementing the interface at all: the boxing class is! If you call s.Set( 24 ) directly from Main(), instead, s is changed, because no polymorphism is involved in this case.
Unfortunately, this tiny little detail cannot be safely hidden. It should be clearly written on basic documentation and taught on introductory courses on C#. Otherwise, programmers will shoot themselves in the foot (although with a small caliber :-).
Hopefully, most C# programmers won't use a structure anyway, and some of them will therefore avoid this bug by sheer luck :-).

Comments:
Letto su punto-informatico

Entro i prossimi decenni gli esperti hanno giĆ  profetizzato l'avvento di auto che potranno fare a meno del guidatore: non resta che augurarsi che, per allora, l'industria hi-tech abbia superato l'annoso problema dei bug nel software.

:-))
 
Post a Comment

<< Home

This page is powered by Blogger. Isn't yours?