Posts Tagged ‘Delphi’

IOUtils.pas – OO File System Access in Delphi 2010

Delphi has long had various functions to let you work with the file system, but one of the new features of Delphi 2010 are some nice wrappers for working with files, paths and directories.

IOUtils.pas is the file in question, and it’s definitely worth a look. Each time I’ve had a look in there during the Field Test I’ve found more interesting stuff to play with. While I’m still exploring it, I thought I’d post a few examples to show you the kind of things you can get up to.

Firstly, something easy:

procedure TForm2.Button2Click(Sender: TObject);  
var   
  Path : string; 
begin 
  if not TDirectory.Exists(edtPath.Text) then 
    Caption := 'Invalid Path' 
  else 
    Caption := edtPath.Text;
 
  ListBox1.Clear;

  for Path in TDirectory.GetFiles(edtPath.Text, edtFilter.Text) do 
    Listbox1.Items.Add(Format('Name = %s', [Path])); 
end;

First we check if the path provided in the edtPath edit box exists. Assuming it does, we then use a for..in statement to get back each file in that directory that matches the filter passed in as the second parameter.

If you look a little further in IOUtils.pas, you’ll see TDirectory has a type declared in it called a TFilterPredicate:

TFilterPredicate = reference to function(const Path: string; const SearchRec: TSearchRec): Boolean;

One of the places it is used is in an overloaded version of GetFiles, like so:

procedure TForm2.Button1Click(Sender: TObject); 
var 
  Path : string; 
  FilterPredicate : TDirectory.TFilterPredicate; 
begin 
  if not TDirectory.Exists(edtPath.Text) then 
    Caption := 'Invalid Path' 
  else 
    Caption := edtPath.Text;

  ListBox1.Clear;

  FilterPredicate := function(const Path: string; const SearchRec: TSearchRec): Boolean 
                     begin 
                       Result := (TPath.MatchesPattern(SearchRec.Name, edtFilter.Text, False)) AND 
                                 (SearchRec.Attr = faArchive); 
                     end;

  for Path in TDirectory.GetFiles(edtPath.Text, FilterPredicate) do 
    Listbox1.Items.Add(Format('Name = %s', [Path])); 
end;

We implement the TFilterPredicate anonymous method to check that each file matches a wildcard pattern and that it also has its Archive flag set. We can then pass it into our call to TDirectory.GetFiles and our code in the for..in statement will only be called for files that match our criteria.

This is all fine for a single directory, but what if we want to walk over the full tree of directories? Easy, just alter our code to look like this:

procedure TForm2.Button3Click(Sender: TObject);
var
  Path, Directory : string;
  FilterPredicate : TDirectory.TFilterPredicate;
begin
  if not TDirectory.Exists(edtPath.Text) then
    Caption := 'Invalid Path'
  else
    Caption := edtPath.Text;

  ListBox1.Clear;

  FilterPredicate := function(const Path: string; const SearchRec: TSearchRec): Boolean
                     begin
                       Result := (TPath.MatchesPattern(SearchRec.Name, edtFilter.Text, False)) AND 
                                 (SearchRec.Attr = faArchive);
                     end;

  for Directory in TDirectory.GetDirectories(edtPath.Text, TSearchOption.soAllDirectories, nil) do
    for Path in TDirectory.GetFiles(Directory, FilterPredicate) do
      Listbox1.Items.Add(Format('Name = %s', [Path]));
end;

We add another for..in statement before the existing one. This one for each Directory in TDirectory.GetDirectories. The second parameter indicates whether we only want to get the top level directories in the path we specify, or whether we want to do all directories , all the way down. In this case I’m specifying the latter. The Third parameter is actually another TDirectory.TFilterPredicate, so we could do further filtering of the directories that match, however I’m passing in nil so all directories will match. Just keep your wits about you while testing, and don’t test it with c:\ as I did

One thing to note, TDirectory.GetDirectories returns child directories of the path we’ve specified (and their child directories, etc if you’ve specified TSearchOption.soAllDirectories), but obviously not the path we pass in itself. That probably sounds obvious, but in this example we’ve subtly altered the behaviour a little, because the files in the directory we specify are not checked anymore. Now you’re aware of this, I’m sure you can figure out how to deal.

There’s a whole bunch more that can be done, such as encrypt files, move directories, etc. IOUtils looks to be a really powerful unit, and is well worth a little playing to get familiar with it.

Generic Interfaces in Delphi

Most of the examples I’ve seen of Generics in Delphi, use classes containing a generic type. However, while working on a personal project, I decided I wanted an Interface containing a generic type.

The project uses an in-process publish/subscribe mechanism, and I wanted a subscriber to have a separate Receive method for each event type, rather than a single method which contained a big case statement with branches for each event type. Equally, however, I didn’t want to have to define an interface type for each event type. What I wanted was to have a generic Subscriber interface, that took the event type as a parameter.

However, I had no idea if I could define a generic Interface, let alone implement one. Even assuming I could do both of those things, would Delphi be able to resolve the correct Receive method to invoke? Only one way to find out….

NB: In this example, I’ve stripped out most of the pub/sub stuff, just leaving the pieces needed to show the generic interfaces. I’ll write about the other pieces over the next few posts.

First, I implemented a few sample events. The contents of these are not that interesting:

TSomeEvent = class
  // other stuff 
end;

TSomeOtherEvent = class
  // other stuff 
end;

Then, I defined a generic interface

ISubscriber = interface
  procedure Receive(Event : T); 
end;

This is the interface that will need to be implemented by my subscribers in order to receive events of a particular type. Note, the type of event is set up as a generic type T.

Then, my subscribers need to implement an interface for each Event type they want to receive, however because it is a generic interface, it’s pretty straight-forward:

TMySubscribingObject = class(TInterfacedObject, ISubscriber, ISubscriber)
protected
  procedure Receive(Event : TSomeEvent); overload; 
  procedure Receive(Event : TSomeOtherEvent); overload; 
end;

Note there’s no definition of a ISomeEventSubscriber and a ISomeOtherEventSubscriber interface, we can just use ISubscriber<T> and pass the type in in-place. We just need to implement the necessary overloaded Receive events.

You can see the rest of the code in the associated test project, but the above shows you the main aim. Implementing multiple interfaces, each with a strongly-typed Receive event, without actually having to define each of those interfaces.

So, does it work? Well, at my first attempt, no, it didn’t. No matter what type of Event I passed in, or via what interface, it always invoked the last Receive method.

dunit_generic_interfacesRule of life #37: If it’s a choice between Malcolm having screwed up or the Delphi compiler architects, it’s probably Malcolm’s fault.

Yes, my bad. Barry Kelly eventually pointed out the error of my ways. I’d originally defined the generic Interface with a GUID. Habit, really. However, this meant that both ISubscriber<TSomeEvent> and ISubscriber<TSomeOtherEvent>, and indeed any other interface I defined off this generic interface, would have the same GUID. This combined with the fact that I was using an “as” cast to get the Interface reference from the TMySubscribingObject instance, confused Delphi into always giving me the same Interface reference.

Drop the GUID and drop the “as”, and it all worked beautifully.

In future posts I’ll show the other side of the equation, the Publisher, as well as the Event Broker in the middle. One other nice side-effect of indicating the events a class is interested in, using interfaces in this way, is that the event broker can simply inspect the interfaces implemented by a subscriber to know which events they are interested in subscribing to.

Storing code in a collection : TDictionary and Anonymous Methods

In my earlier post, I mentioned you could use the new generic collection classes in Delphi 2009 to store anonymous methods instead of data. I wanted to try this out so I came up with the scenario of implementing factory classes as an excuse to experiment.

Factories can be a very useful way to centralise the creation logic for classes. Very often you see it when you have a base class and a whole bunch of different descendant classes and you want to do the creation in one place. Let’s say you have a reporting application. You may have a base report and a bunch of descendant reports, and you want to enforce some consistency over property values for the different reports that are set at the time of creation. Having these spread around every different place a report could be created may be error-prone. Alternatively, maybe you want to get away from the massive if…then..else blocks you often see in this situation. A Report Factory can give you one place to make sure all reports get created in the certain way, and also let the client code simply request a report by name and have the correct concrete report class created for them.

Problem is usually this means writing a new factory each time you want this, plus creating factory mapping classes to register each concrete report. This is not only a whole bunch of effort and unnecessary classes, it’s more new code to debug.

Instead, I thought I’d try creating a single factory class, that uses Generics to allow you to change the key used to request an instance, and that uses Anonymous Methods to do the creating so as to avoid the necessity for mapping classes. Sticking with my report scenario above, here’s how I wanted to instantiate it:

  ReportFactory := TFactory.Create;

Where the first generic parameter is the type of the key I will use to request a report, in this case a string representing the report name, and the second is the base type I want the Factory to return.

Here’s how I wanted to register new concrete implementations:

  ReportFactory.RegisterFactoryMethod('Malcolm''s Report',
                  function : TBaseReport
                  begin
                    Result := TReportFlexible.Create(rnReportMalcolm);
                    // other property setting/setup code
                  end);

You simply pass in the key value and the anonymous method that will be invoked to do the creation. Lastly, I want to retrieve a particular instance of my report by calling GetInstance on the factory passing in the key value.

  Report := ReportFactory.GetInstance('Malcolm''s Report');

Remember however, the type of the key value and the base type of the returned objects should be configurable via generic parameters.

Here’s the class definition I came up with:

  TFactoryMethod = reference to function : TBaseType;

  TFactory = class
  private
    FFactoryMethods : TDictionary>;
    function GetCount: Integer;
  public
    constructor Create;
    destructor Destroy; override;
    property Count : Integer read GetCount;
    procedure RegisterFactoryMethod(Key : TKey;
                      FactoryMethod : TFactoryMethod);
    procedure UnregisterFactoryMethod(Key : TKey);
    function IsRegistered (Key : TKey) : boolean;
    function GetInstance(Key : TKey) : TBaseType;
  end;

As you can see, TKey is the name of the generic parameter representing the type I want to use to request a concrete instance of the base type specified in TBaseType.  RegisterFactoryMethod lets you register an anonymous method of type TFactoryMethod which as we already discussed will do the creating, and there is a matching UnregisterFactoryMethod to remove a key\method pair. GetInstance is there, taking the generic parameter TKey and returning an instance of the base type. There are also a couple of methods to check if a particular key is already registered and another to return the number of registered keys.

Nothing there is terribly interesting if you’ve already played with generic types. However, for me the interesting part is I’m storing the key/method pairs in a TDictionary<TKey, TFactoryMethod<TBaseType>>.  Storing the anonymous method and the key in the dictionary is pretty straightforward:

procedure TFactory.RegisterFactoryMethod(Key: TKey;
  FactoryMethod: TFactoryMethod);
begin
  if IsRegistered(Key) then
    raise TFactoryMethodKeyAlreadyRegisteredException.Create('');

  FFactoryMethods.Add(Key, FactoryMethod);
end;

and retrieving it and executing it is equally so.

function TFactory.GetInstance(Key: TKey): TBaseType;
var
  FactoryMethod : TFactoryMethod;
begin
  if not IsRegistered(Key) then
    raise TFactoryMethodKeyNotRegisteredException.Create('');

  FactoryMethod := FFactoryMethods.Items[Key];
  if Assigned(FactoryMethod) then
    Result := FactoryMethod;
end;

With that, we’ve got a very reusable Factory class, so I should have no excuse for avoiding them in the future. Of course, this is a fairly simplistic implementation, it doesn’t handle pipelining during the creation process for example, but this would be relatively straight-forward to add with more anonymous methods stored in a TList<T> for example.

Anyway, hopefully this little experiment has sparked some thoughts in you. The source for both the class and a client implemented as unit tests is available from my delphi-experiments repository on github.

Meanwhile I’m off to do some more playing. Next post will probably be the Pooling example I mentioned earlier but somehow forgot to post.

A TDictionary explanation

On the recent Delphi 2009 roadshow in Australia, I had a few people ask me about the new TDictionary<TKey, TValue> container in Generics.Collections. If you haven’t played with it yet,  Roland Beenhakker has a nice write up on using it, but most of the questions I was getting were not about how to use it, but about what it’s for.

The way I try and explain it is by stepping back and looking at collections that people already are used to. Leaving implementation details aside, a lot of the Delphi collections store values and let you retrieve those values using a key.

For example, a TStringList lets you store strings indexed by an integer value. So in this case, the value would be a string and the key would be an integer. A TList lets you store pointers and retrieve them using an integer. Again, the key is an Integer, the value is a pointer.

The next step is to think about TList<T>. It’s not that different, the key is still an integer, just the value can be parameterised using generics.

If you’re ok with that, then a TDictionary<TKey, TValue> is pretty easy to understand. It’s a generic container that lets you parameterise not only the value, but the key also. So, to get somewhat similar behaviour to a TStringList, you could use a TDictionary<integer, string>. Now, of course the implementation details of how TDictionary would store those strings is wildly different to a TStringList, but in terms of understanding it, I think it helps.

Equally, a TList<T> might be replaced in broad terms with a TDictionary<Integer, T>, with T being whatever type you like.

The really cool thing though, is that the key part does not have to be an Integer. You could instead of TDictionary<Integer, String> do something like TDictionary<string, string>, so a string could be retrieved by using another string as a key. If you’re trying to think of an example of that, what about the TDictionary’s real world namesake, a dictionary. The key string would be the word, the definition would be the Value.

Further, it could be TDictionary<TGuid, TState>, where you might be using a GUID as a sessionID to retrieve an object that stores your session state in some sort of distributed system.

An interesting take on this is to use a TDictionary to store code in the form of anonymous methods. Anonymous methods are defined as types, so no reason they can’t be stored in a collection just like data. I find this really interesting. If you’re a fan of using table-driven development to replace convoluted if..then…else or case statements, this might spark some ideas. In fact in my next blog post I’ll show an example of a generic Factory implementation that uses a TDictionary internally to store anonymous factory methods.

Anyway, there is a lot more to know about TDictionary’s, such as hashing functions, comparers, etc, but hopefully this has at least given you a high-level understanding of where you might look at using them.

Delphi 2009 : TQueue<T>.Dequeue vs TObjectQueue<T>.Dequeue

I was poking around inside Generics.Collections the other day while writing some code for a future post on pooling, and I came up against something puzzling.

Generics.Collections has a class called TQueue<T>, which is a generic version of the classic first-in-first-out datastructure. The nice thing of course is that it can now be made type-safe by specifying a type parameter. If you’ve ever looked at a queue, such as the non-generic one that ships with Delphi already, or even thought about a queue datastructure, looking at TQueue<T> will not hold many surprises.

Generics.Collections also has a class called TObjectQueue<T>, which descends from TQueue<T> and adds object ownership (ie. the queue will destroy any objects it holds when you destroy it). This is implemented using an interesting looking notification process that probably deserves investigation in a future post, but again, the object ownership concept should be one many Delphi developers would be familiar with from TObjectList, etc, in previous versions of Delphi.

TQueue<T> has a function called Dequeue which returns the front item off the queue. The signature looks like this:

function TQueue.Dequeue: T;

However, when I came to use TObjectQueue<T> its Dequeue method looks like this:

 

procedure TObjectQueue.Dequeue;

That’s right, a procedure. Internally it still takes the front item off the queue, in fact all it does is call the inherited TQueue<T>.Dequeue, but it doesn’t give it back to you.

I have to admit, my initial reaction to this was to assume I’d navigated to the wrong class so I went back to the starting point and walked my way through it again. Nope, still a procedure. No matter how I navigated to it, it wouldn’t turn into a function.

At this point I took the dogs for a walk and thought about it some more. When I got back, unfortunately, it was still a procedure. Further, after working through the notification code a little, it turns out that not only does TObjectQueue<T>.Dequeue take the front item off the queue and not give it back to you, it also Frees the item.

Now, I’d love to tell you that after careful examination of the rest of the code I discovered what was going on but that would be complete bollocks. Instead, I dashed off an email to Barry Kelly.

The answer comes down to the fact that because TObjectQueue<T> owns the objects, you need the ability to do two different things:

  • Remove the front item from the queue and have it automatically destroyed by its owner, the queue. Maybe this is after you’ve looked at it using Peek and decided you don’t want it. This is what TObjectQueue<T>.Dequeue is for.
  • Remove the front item from the queue, and have it returned to you. Part of this is that it will be removed from the queue’s ownership and it becomes your responsibility to free. This is what the method Extract is for, which to be honest I didn’t notice was there, and is in fact defined up on the TQueue<T> as well.

If you think about the names Dequeue vs Extract, this kind of makes sense. Some small part of me still finds it a little disconcerting that a function turns into a procedure between TQueue<T> and TObjectQueue<T>, but I’m mostly over it by now. Chances are if I’d stumbled upon Extract before Dequeue, none of this would have occurred. Anyway, I changed my pool code to use Extract instead of Dequeue, all my unit tests passed and I was free to go watch the Brisbane Broncos get robbed of a spot in the Rugby League finals.

Anyway, I hope this will help some future poor soul googling away to try and find out why Dequeue changes from a function to a procedure.

Anonymous Methods, Generics and Enumerators

I’ve been playing around with Anonymous Methods in Delphi 2009 a little bit lately, and I thought one of my experiments might be worth sharing.

I decided I would try to extend TList<T> so that when you enumerate over it in a for..in loop, not every item would be returned. Specifically, only items that passed a filter criteria would be returned, and I would use an anonymous method to specify the filter criteria.

This actually turned out to be kind of fun, as I got to play with Enumerators, Generics and Anonymous Methods all in one fairly short piece of code.

So, first I looked at TList<T>. I was hoping there was some way I could specify that it should use a different Enumerator than the default, but unfortunately I could not find a way. So I descended from TList<T> to create a TFilteredList<T> to:

  • add a couple of methods, ClearFilter and SetFilter, which I’ll come back to later
  • define TFilteredEnumerator<T>
  • reintroduce the GetEnumerator method to create an instance of TFilteredEnumerator<T> instead of TList<T>’s standard TEnumerator<T>

I also declared an anonymous method type called TFilterFunction<T> which takes a parameter of type T and returns true or false depending on whether the parameter passed or failed the filter criteria respectively.

Here’s the definition:

  TFilterFunction = reference to function(Item : T) : boolean;
  TFilteredList = class(TList)
  private
    FFilterFunction: TFilterFunction;
  public
  type
      TFilteredEnumerator = class(TEnumerator)
      private
        FList: TFilteredList;
        FIndex: Integer;
        FFilterFunction : TFilterFunction;
        function GetCurrent: T;
      protected
        function DoGetCurrent: T; override;
        function DoMoveNext: Boolean; override;
        function IsLast : Boolean;
        function IsEOL : Boolean;
        function ShouldIncludeItem : Boolean;
      public
        constructor Create(AList: TFilteredList;
                           AFilterFunction : TFilterFunction);
        property Current: T read GetCurrent;
        function MoveNext: Boolean;
      end;
    function GetEnumerator: TFilteredEnumerator; reintroduce;
    procedure SetFilter(AFilterFunction : TFilterFunction);
    procedure ClearFilter;
  end;

Most of the methods on TFilteredEnumerator<T> are not terribly interesting. The constructor takes a reference to an instance of our TFilterFunction<T> anonymous method, which it stores in the FFilterFunction field. This constructor gets called from the aforementioned GetEnumerator method of TFilteredList<T>.

Most of the hard work is done by the MoveNext method, which looks like this:

function TFilteredList.TFilteredEnumerator.MoveNext: Boolean;
begin
  if IsLast then
    Exit(False);

  repeat
    Inc(FIndex);
  until isEOL or ShouldIncludeItem;

  Result := not IsEol;
end;

It is invoked when the Enumerator wants to move to the next item in the List, returning True if it does this successfully, otherwise returning False.  In this case, it:

  • first checks to see if we’re already at the end of the List, in which case it bails out returning False, we’re at the end of the list.
  • otherwise, it keeps incrementing the position until either we’re passed the last item or we hit an item that passes our filter criteria.

ShouldIncludeItem invokes our FIlterMethod if one is defined, passing in the current item in the list. It looks like:

function TFilteredList.TFilteredEnumerator.ShouldIncludeItem: Boolean;
begin
  Result := True;
  if Assigned(FFilterFunction) then
    Result := FFilterFunction(FList[FIndex]);
end;

The following code creates a TList<TPerson> and loads it up, then enumerates over each TPerson in the list that is older than 18. You can see the call to SetFilter where we specify the anonymous method that will do the actual filtering. It then clears the filter and enumerates over all the TPerson objects.

 

    PersonList := TFilteredList.Create;
    try
      PersonList.Add(TPerson.Create('Fred Adult', 37));
      PersonList.Add(TPerson.Create('Julie Child', 15));
      PersonList.Add(TPerson.Create('Mary Adult', 18));
      PersonList.Add(TPerson.Create('James Child', 12));

      writeln('--------------- Filtered Person List --------------- ');
      PersonList.SetFilter(function(Item : TPerson): boolean
                           begin
                             Result := Item.Age >= 18;
                           end);

      for P in PersonList do
      begin
        writeln(P.ToString);
      end;

      writeln('--------------- Unfiltered Person List --------------- ');
      PersonList.ClearFilter;
      for P in PersonList do
      begin
        writeln(P.ToString);
      end;

    finally
      PersonList.Free;
    end;

This produces the following output:

————— Filtered Person List —————

Name = Fred Adult, Age = 37

Name = Mary Adult, Age = 18

————— Unfiltered Person List ————-

Name = Fred Adult, Age = 37

Name = Julie Child, Age = 15

Name = Mary Adult, Age = 18

Name = James Child, Age = 12

The generics come into play so that I can use the same list for something other than TPerson objects, in the example below, Integers:

    IntegerList := TFilteredList.Create;
    try
      IntegerList.Add(1);
      IntegerList.Add(2);
      IntegerList.Add(3);
      IntegerList.Add(4);

      writeln('--------------- Filtered Integer List --------------- ');
      IntegerList.SetFilter(function(Item : Integer): boolean
                            begin
                               Result := Item >= 3;
                            end);

      for I in IntegerList do
      begin
        writeln(IntToStr(I));
      end;

      writeln('--------------- Unfiltered Integer List --------------- ');
      IntegerList.ClearFilter;

      for I in IntegerList do
      begin
        writeln(IntToStr(I));
      end;

    finally
      IntegerList.Free;
    end;

Now, I’m not sure that I really want to write my code like this. I like the fact that there’s a nice separation between the code that decides which items to act on, and the code that actually acts on them, rather than having them all jumbled together inside the for..in loop.

Ironically, that’s also the bit that I don’t like, as I can see that the filter code could possibly be overlooked by someone not familiar with anonymous methods, and think that we were acting on all the items in the list.

Yet again, I want to have my cake and eat it to.

However, as I said at the start this was an experiment to teach me a bit more about anonymous methods, so from that point of view it worked, and hopefully you got something out of it as well.

You can download the code from my delphi-experiments repository on github.

Incremental Searching in the Delphi Object Inspector

The sound of a massive forehead slap reverberated around my office earlier today. I think you can still see finger marks on my head.

I’ve been using Delphi for around 13 years, since sometime during the beta program for Delphi 1. I’ll be the first to admit I don’t know everything about the product, but I’ve always tried to learn keyboard shortcuts and anything that will help me navigate my way around the IDE. Even now when I don’t code day-in-day-out, I’m pretty sure I could show an experienced Delphi guy a thing or two.

However, my pride has taken a bit of a dent. I was suggesting some features to the Delphi team today. One of the features I was requesting was incremental filtering  of properties in the Object Inspector, like you can do currently with the Tool Palette. I’ve already mentioned here before that I love the Tool Palette filtering, so much so that I’ve written OpenTools add-ins to let me use it on more stuff. So, I figured this would really help me when I’m having trouble finding a property (especially when the Object Inspector is not sorted alphabetically)
Well, while the filtering isn’t there today, turns out incremental search is, and has been for, oh, 13 years or so (ie. since Delphi 1). Doh!

So here goes, I’m either about to discover that I’m the only Delphi user in the universe who doesn’t know this, or the sound of forehead slaps is about to echo around the world.

  • Press F11 until you have the Object Inspector selected.
  • This will have the caret positioned in the "value" bit of the Object Inspector (ie. the right hand column where the property values go, not the left hand column where the name of the property is).
  • That’s not where we want it, so press the TAB key to position the caret in the "name" column
  • Start typing the name of the property you want. The selection will move down to the closest matching property name as you type.
  • Pressing TAB again will move the caret back to the "value" column so you can start editing the property value.

Here’s a little movie to show you it in action.

So assuming some of you didn’t know this, I’m glad my embarrassment has served some useful purpose 🙂

Nullable Types in Delphi

I was traveling a bunch over the last two weeks, USA, Japan and Korea. In Osaka, I did a session on Generics for the Japan Developer Camp.

Afterwards, I was having a further play with Highlander and Generics and stumbled across something rather nice. Maybe everyone else is aware of this already, but the new Generics support in Delphi for .NET also gives us access to Nullable types. So the following code:

var
MyInt : Nullable<Integer>;
begin
Listbox1.Items.Add(MyInt.HasValue.ToString);
MyInt := 18;
Listbox1.Items.Add(MyInt.HasValue.ToString);
Listbox1.Items.Add(MyInt.Value.ToString);
end;

produces the following output in the Listbox:

false

true

18

I’m currently reading through Professional .NET 2.0 Generics to get a better understanding of all the possibilities, but already I’m getting excited by what this allows. I’ll post more as I explore further.


Now, all we need do is to deliver Tiburon to give me Generics in Win32, and I’ll be a happy boy.

Update : Hallvard has written up a good overview of generics in Highlander here