Why setlength is evil™

There has been a lot of discussion on our private Chrome beta newsgroups lately
about the pros and cons of providing a Run Time Library (RTL) with Chrome in addition
to what’s provided by the FCL, and the general sensibility of certain methods in

Among them, the SetLength() method of the Delphi RTL is one that a couple of people
involved in porting their Delphi projects to Chrome have missed, and this has sparked
some heated debates. Even leaving the question of whether a RTL is useful and needed
behind (and the ShineOn open source project is taking care of providing a RTL for
Chrome to ease the porting of Delphi applications, for those that want or need it),
it is my opinion that a SetLength() for .NET is just plain wrong. And here is why:

As a little background for those not too familiar with the Delphi RTL, the SetLength()
method comes in two flavors, one for Strings and one for Arrays. In both cases you
call it passing your object (i.e. a string or a dynamic array) and the desired size
– and the function will resize your object as needed.

Now let’s consider this from a .NET perspective:

Strings, represented by the System.String class, are always read-only
in .NET. You can instantiate a new string object with any value, but once you hold
that object, it cannot be changed. So what is SetLength() supposed to do? The only
sensible implementation would be to return a new (again, read-only) string object
that was either cut off, or contains additional empty/garbage characters at the
end. The first task is handled just fine by the String.SubString method,
and the second is just plain useless. In Delphi/Win32, you would usually call SetLength()
to enlarge the string and subsequently fill the new characters with values – but
since .NET strings are read-only, that’s impossible. So what would you do with the
the enlarged string?

Arrays are represented in .NET as descendants of the System.Array
class. In contrast to strings, arrays are modifiable, so on first sight, it does
seem to make sense to resize arrays to make more room, and then fill the new items.
But there’s one catch: The System.Array class doesn’t allow arrays to be resized.
In .NET, the size of an array is determined when it is created, and cannot be modified
later-on. And this decision made by the framework designers does make a lot of sense
to anyone who bothered listening to their Data Structures 101 in college:
Arrays are a useful data structure and perform well for a number of tasks. Growing
dynamically is not one of them.

So, what are our alternatives? Is it possible to provide a SetLength() method as
part of a RTL or as compiler magic function? It sure is (and in fact, Delphi for
.NET does provide it). But how would such a method work? Basically, it would need
to create a new array of the appropriate size, and then copy all the items
over. Again, anyone who remembers Algorithms 101 is starting to feel uneasy
now, as this is an O(n) operation, and will get more costly the larger the array.

What’s more, not only would SetLength() be a very costly method, but it would also
give the wrong impression of being a pretty cheap (not to mention common)
operation. This in turn would give people incentive to write code like this:

method Foo(aInput: TextReader); var lData: array of string := new string[0]; begin s := aInput.ReadLine(); while assigned(s) do begin SetLength(lData, lData.Length+1); lData[lData.Length-1] := s; s := aInput.Readine(); end; end;

and then go off to non-tech and complain about "how slow .NET is".

I think rather then giving users the comfort zone of porting their code to a new
platform without thinking (and encouraging people writing new code to write it badly),
a language should not hide the costs of doing certain things from the user. Once
people see that in order to make their above code work they need to manually create
a new array and call Array.CopyTo(), it is only a small step for them to realize
the underlying problem and switch to a more suitable data structure, altogether.

And the end result will be .NET applications that perform well. Bad news for the
guys on non-tech – good news for the users of the applications ;).

marc hoffman

Chief Architect and CEO here at RemObjects Software. Project Manager for Elements and lead developer of Fire, our awesome new development environment for the Mac.