The other day, I presented an introduction to the new parameterized types language feature in the latest release of Delphi for .NET at the Developer Days event. First of all, I’m not what one would call a consummate expert in the realm of language generics. In fact a lot of it just makes my head hurt. For real hard-core, on the edge, looks at the kinds of things you can do with generics, you should be looking here or here. I’m sure you may recognize those guys that run those blogs.
So given that disclaimer, I’m going to start presenting some topics on using the new parameterized types in the Delphi language. Keep in mind that for this release this only applies to Delphi for .NET. Check out the roadmap for some guidance on when this will be coming to the Win32 side of the house. I’m going to approach this from the perspective that as a Delphi programmer, you’ve never used generics or parameterized type. You may not even know what they are, how to use them, and why. That’s ok… Me neither.
In many ways,"generic" programming takes the code reuse tenet of Object Oriented Programming (OOP) to a whole new level. When OOP was becoming more prominent throughout the ’80s, a key advantage to the concept was this notion of being able to reuse and extend already written code in ways not considered by the original author. Instead of reusing code via inheritance and polymorphism (although parameterized types strongly leverage OOP), it is handled by separating the type of the data being operated on from the algorithm.
Suppose you’ve written a killer new sorting algorithm. Now you want to use that algorithm to sort a variety of different types of items. In one case you want to sort a simple list of integers. Other times you may want to sort some complex data structure based on a collating sequence unique to that data. Without "generics" you probably would have written this algorithm using traditional OOP polymorphism. So when you wanted to use your killer sort function, you’d have to create a unique descendant of the base class that implements the algorithm, override a few methods, and finally create an instance of this new type to use it.
Generics allow you to, in many cases, forgo the sometimes tedious, often mind-numbing, task of creating yet another descendant class just to sort that new data structure you just defined. With a parameterized type, you write your algorithm as if you were writing it for a specific data-type (yes, I know there is a little more you have to consider, this is the basic gist of it). So let’s start with something simple.
So I find myself constantly needing to reverse the items in an array. I have this awesome algorithm that I keep writing over and over. Sometimes I need to reverse an array of integers and other times it’s strings. So, how could I use parameterized types to only write this killer algorithm only once and then tell the compiler that I need a version to work with integers, or strings, or some other structure only when needed. Here’s what I came up with:
type TArrayReverser<T> = class procedure ReverseIt(var A: array of T); end; procedure TArrayReverser<T>.ReverseIt(var A: array of T); var I: Integer; E: T; begin for I := Low(A) to High(A) div 2 do begin E := A[I]; A[I] := A[High(A) - I]; A[High(A) - I] := E; end; end;
Eat your heart out, Mr. Knuth ;-). Now I can actually use the above parameterized type anyplace that I want to have the order of an array reversed, regardless of what the element types and sizes are. I don’t have to create a descendant class and override some methods. I just instantiate the above type using the type of the array elements as the parameter. For example:
var I: Integer; IntegerArray: array of Integer; IntegerArrayReverser: TArrayReverser<Integer>; begin SetLength(IntegerArray, 10); for I := Low(IntegerArray) to High(IntegerArray) do IntegerArray[I] := I * 10; IntegerArrayReverser := TArrayReverser<Integer>.Create; IntegerArrayReverser.ReverseIt(IntegerArray); for I := Low(IntegerArray) to High(IntegerArray) do Writeln('IntegerArray[', I, '] = ', IntegerArray[I]); end;
So there is a quick intro to using parameterized types in Delphi for .NET. I’m sure you can see some of the interesting things you can do with this new found power. Another interesting fact about using generics is that in many cases the code is actually more compact and faster than if you’d tried to make a general purpose class using traditional OOP techniques. This is because the body of the ReverseIt procedure is compiled (at runtime in .NET, at compile-time for Win32) as if ‘T’ were actually declared as an ‘Integer’. We could take the class I wrote above and use it to reverse an array of TPoint or TRect structures. Any type you declare or an intrinsic language type can be used to create a concrete version of TArrayReverser. If you have any questions about this pose, please post them in a comment.Posted by Allen Bauer on September 13th, 2007 under CodeGear, Delphi, Generics, Work |