Click here to Skip to main content
Click here to Skip to main content

Introduction to Delphi Prism

, 18 Jun 2009
Rate this:
Please Sign up or sign in to vote.
First steps tutorial for Delphi developers.

Introduction

This article is for Delphi developers who want to learn Prism. It covers the most significant changes to the old Delphi language and the basics about the new parallel computing support.

Delphi Prism is a .NET language based upon Delphi. You might say it is the Delphi.NET. The name also stands for a Visual Studio extension that makes coding in Prism comfortable. It is a Visual Studio Shell application, so it integrates into an existing VS 2008 (if it is installed on the machine) or runs as a standalone IDE.

Why should you use Prism at all? First, there are Linux and Macintosh. Maybe you want to write platform independent code for Mono. Second, desktop applications are not always adequate and Delphi's ISAPI support is not state of the art. Third, you might be switching from native to managed development in general. Learning to use a new platform will be easier with a language you already know.

There's a fourth possible reason: Your applications are designed for multicore computers. Delphi Prism is designed especially for heavy multithreading and high responsiveness.

In this absolute beginner's tutorial, we'll write an address book using the Lucene.NET library. To introduce desktop and web at the same time, there'll be a WPF application to fill the search index and a web page to search it.

Index Builder: Your First WPF Project

Create a new Delphi WPF project. Visual Studio shows you the Forms Designer, where you can design the main dialog as usual. Change to the code editor. Delphi developers will be surprised: instead of a unit the code generator has declared a namespace. In the class definition, you find the new keywords partial and method. The latter replaces procedure as well as function, because .NET doesn't make a difference between those two. (Rather every method has a return value, for procedures it is void.) The name Create for constructors is replaced by nothing at all: objects get created with new, just as in the C-like languages.

namespace LuceneTest;

interface

uses
  System.Linq,
  System.Windows,
  System.Collections.ObjectModel,
  System.Collections.Generic,
  Lucene.Net.Index,
  Lucene.Net.Store,
  Lucene.Net.Search,
  Lucene.Net.Documents,
  Lucene.Net.Analysis.Standard,
  Lucene.Net.QueryParsers;

type
  Window1 = public partial class(System.Windows.Window)
  private
    method btnIndex_Click(sender: System.Object; e: RoutedEventArgs);
    method btnSearch_Click(sender: System.Object; e: RoutedEventArgs);
    method addDocument(name: String; address: String; writer: IndexWriter);
  public
    constructor;
  end;

Let's begin with the address book. A list of names has to be filtered and added to the search index. The new way to search lists looks quite confusing: the common Delphi developer is used to declare all variables above the procedure! But thanks to LINQ, there are lists without a defined type (that means, not defined in code), which don't even have to be declared.

method Window1.BuildSearchIndex;
var
  persons: Collection<person>;
  directory: Directory;
  writer: IndexWriter;
  analyzer: StandardAnalyzer;
begin
  persons := GetPersons;
  var filteredPersons := from n in persons
                       where (not n.Blocked)
                       order by n
                       select n;

  directory := FSDirectory.GetDirectory('L:\\Index', true);
  analyzer := new StandardAnalyzer();
  writer := new IndexWriter(directory, analyzer, true);

  for each person in IEnumerable<person>(filteredPersons) do
  begin
    addDocument(person.Name, person.Address, writer);
  end;

  writer.Optimize();
  writer.Close();
end;

Index Searcher: Your First Web Project

The newly built search index is to be searched by a web form. Create a new Delphi ASP.NET project! Again, Visual Studio shows you a designer window. Open the code behind to meet another new keyword. partial splits the Delphi class into an editable file Default.aspx.pas and the designer generated file Default.aspx.designer.pas. While you're typing ASP code, the designer fill its file with all necessary declarations. Your file will rarely be touched by the designer, unless the generated code must be edited by you. Now, double-click a button on the web form to insert an event handler.

By the way, this is a good point to introduce the double compare operator. You can make your code more readable using "0.2 <= score < 0.5" instead of the long version "0.2 <= score and score < 0.5".

method _Default.btnFind_Click(sender: Object; e: EventArgs);
var
  indexDirectory: String;
  parser: QueryParser;
  query: Query;
  searcher: IndexSearcher;
  searchResults: TopDocs;
  currentDocument: Document;
  lblResult: Label;
begin
  parser := new QueryParser('name', new StandardAnalyzer());
  query := parser.Parse(txtName.Text);
  indexDirectory := ConfigurationManager.AppSettings['LuceneIndexDirectory'];
  searcher := new IndexSearcher(FSDirectory.GetDirectory(indexDirectory, false));  
  searchResults := searcher.Search(query, nil, 10);

  for each currentResult in searchResults.scoreDocs do
  begin
    currentDocument := searcher.Reader.Document(currentResult.doc);
    lblResult := new Label();
    lblResult.Text := string.Format("{0} - {1}, {2}",
        ScoreToString(currentResult.score),
        currentDocument.Get('name'),
        currentDocument.Get('address'));
    pResult.Controls.Add(lblResult);
  end;
  pResult.Visible := true;
end;

method _Default.ScoreToString(score: Single): String;
begin
    Result := 'Impossible';
    if(0.2 <= score < 0.5)then
        Result := 'Improbable'
    else if(0.5 <= score < 0.8)then
        Result := 'Maybe'
    else if(0.8 <= score < 1.0)then
        Result := 'Quite sure'
    else
        Result := 'That is it!';
end;

More CPUs, Please!

Performance has always been an argument for using Delphi. Prism focuses on parallel programming and scores with handy new keywords which save lots of code and chaos.

The most handy one is async which simply declares a method as asynchronous. An asynchronous method is executed in a separate thread - automatically! That way a single word in the declaration line can prevent the main thread from being slowed down by log output calls. In the declaration of a variable, async is the promise that the variable will - in the future - contain a certain value. The value itself is calculated in a separate thread. If another thread accesses the variable before the calculation is finished, that thread is blocked until the value is available. But the most impressive news is the asynchronous block:

01 SomethingInTheMainThread(a);
02 var myBlock: future := async
03 begin
04    DoSomethingLong(b);
05    DoEvenMore(c);
06 end;
07 WholeLotInTheMainThread(d);
08 myBlock();

When is line 04 executed? Assuming there are enough processors, simultaneously with line 07. async makes the block run in another thread. The future variable myBlock is your handle for that thread, so you can wait for it in line 08. The method call myBlock() doesn't execute the block! Only if it hasn't yet finished, it waits for the thread to end.

Inside the asynchronous blocks, you may call other asynchronous methods. But be careful: the main thread doesn't wait for them. Waiting for an asynchronous block means waiting for this one thread, not for any "nested" threads that have been started by the block.

To visualise all that parallel stuff, the web page contains four checkboxes. They'll be evaluated in the background, when the user already sees the response page in his browser.

type
  _Default = public partial class(System.Web.UI.Page)
  protected 
      method btnFind_Click(sender: Object; e: EventArgs);
      method btnSurvey_Click(sender: Object; e: EventArgs);
  private
      logFile: String;
      logWriter: future StreamWriter := new StreamWriter(logFile);
      method ScoreToString(score: Single): String;
      method WriteLog(i: Integer; text: String); async;
  end;

implementation

method _Default.btnSurvey_Click(sender: Object; e: EventArgs);
begin
    logFile := ConfigurationManager.AppSettings['LogFile'];
    logWriter.AutoFlush := true;

    var backgroundWork: future := async
    begin
        logWriter.WriteLine("Loop starts at {0:mm:ss:fff}.", DateTime.Now);
        for each matching chkSurvey: CheckBox in pSurvey.Controls index i do
            if(chkSurvey.Checked)then
                WriteLog(i, chkSurvey.Text);

        logWriter.WriteLine("Loop stops at {0:mm:ss:fff}.", DateTime.Now);
    end;

    backgroundWork();
    logWriter.WriteLine("Page delivered at {0:mm:ss:fff}.", DateTime.Now);
end;

method _Default.WriteLog(i: Integer; text: String);
begin
    Thread.Sleep(1000);
    logWriter.WriteLine("{0}. Selection is '{1}' at {2:mm:ss:fff}.", i, text, DateTime.Now);
end;

The StreamWriter seems to be initialised with an empty path. But the keyword future causes the assignment in the declaration line to be executed shortly before the first access to the object. At that time, the path already is assigned. The method WriteLog is async, so it always runs in the background.

In the async block's thread, a for each matching loop iterates through all controls that are checkboxes. Every iteration can call another asynchronous method. When in the end the main thread waits for the block to finish, it waits only for the block's thread, not for any additional thread started by the block. The WriteLog calls can still run in the background, maybe waiting for a free CPU, when the user already sees the complete response.

The Source

To compile the demo applications, you need the assembly Lucene.Net.dll. If the latest stable release doesn't work, try version 2.0.004.

A 30 days trial version of Delphi Prism can be downloaded from Embarcadero. Sadly, they don't offer a free Express version.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Corinna John
Software Developer
Germany Germany
Corinna lives in Hannover/Germany (CeBIT City) and works as a Delphi developer, though her favorite language is C#.

Comments and Discussions

 
GeneralMy vote of 5 PinmemberAmir Mahfoozi11-Jan-13 19:30 
GeneralNice PinmemberMike Ellison30-Jun-09 6:17 
GeneralRe: Nice PinmemberCorinna John30-Jun-09 10:59 
QuestionRe: Nice PinmemberDavid Wachtel6-Dec-10 20:06 
GeneralThey have a free compiler available Pinmemberlextm28-Jun-09 19:35 
GeneralRe: They have a free compiler available PinmemberCorinna John30-Jun-09 11:01 
Generaloh,Im so glad. PinmemberJunKong Oun18-Jun-09 22:38 
GeneralRe: oh,Im so glad. PinmemberCorinna John18-Jun-09 22:59 
GeneralRe: oh,Im so glad. PinmemberJunKong Oun19-Jun-09 15:49 
GeneralRe: oh,Im so glad. PinmemberCorinna John21-Jun-09 9:02 
GeneralRe: oh,Im so glad. PinsitebuilderUwe Keim24-Jun-09 23:41 
GeneralRe: oh,Im so glad. PinmemberJunKong Oun30-Jun-09 22:56 
GeneralRe: oh,Im so glad. PinmemberBlue_Boy23-Jul-09 21:15 
GeneralNice article PinmemberCristi2k318-Jun-09 1:28 
GeneralRe: Nice article PinmemberCorinna John18-Jun-09 2:29 
GeneralRe: Nice article PinmemberCristi2k318-Jun-09 23:29 
GeneralSmall Typo Pinmemberpeterchen17-Jun-09 21:48 
GeneralRe: Small Typo PinmemberCorinna John18-Jun-09 0:04 
Thanks for the hint. Smile | :)
I've corrected the typo and added a paragraph about what Prism is.
 
This statement is false.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140827.1 | Last Updated 18 Jun 2009
Article Copyright 2009 by Corinna John
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid