65.9K
CodeProject 正在变化。 阅读更多。
Home

Delphi Prism 入门

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.91/5 (12投票s)

2009年6月17日

CPOL

5分钟阅读

viewsIcon

92070

downloadIcon

349

Delphi 开发者的入门教程。

引言

本文档面向希望学习 Prism 的 Delphi 开发人员。它涵盖了旧 Delphi 语言最重要的变化以及关于新的并行计算支持的基础知识。

Delphi Prism 是一种基于 Delphi 的 .NET 语言。你可以称它为Delphi.NET。该名称还代表了一个 Visual Studio 扩展,使得在 Prism 中编码更加舒适。它是一个Visual Studio Shell应用程序,因此它可以集成到现有的 VS 2008(如果已安装)中,或者作为独立的 IDE 运行。

为什么要使用 Prism?首先,有 Linux 和 Macintosh。也许你想编写跨平台的 Mono 代码。其次,桌面应用程序并非总是足够,而 Delphi 的 ISAPI 支持并非最先进。第三,你可能正在从原生开发转向托管开发。使用熟悉的语言学习新平台会更容易。

还有第四个可能的原因:你的应用程序是为多核计算机设计的。Delphi Prism 专门为重度多线程和高响应性而设计。

在这个绝对的入门教程中,我们将使用 Lucene.NET 库编写一个地址簿。为了同时介绍桌面和 Web 应用,我们将创建一个 WPF 应用程序来填充搜索索引,以及一个网页来搜索它。

索引生成器:你的第一个 WPF 项目

创建一个新的 Delphi WPF 项目。Visual Studio 会显示窗体设计器,你可以在其中像往常一样设计主对话框。切换到代码编辑器。Delphi 开发人员会感到惊讶:代码生成器声明了一个 namespace 而不是 unit。在类定义中,你会发现新的关键字 partialmethod。后者取代了 procedurefunction,因为 .NET 并没有区分这两者。(更确切地说,每个方法都有返回值,对于 procedure 它是 void。)构造函数的名称 Create 被完全省略了:对象使用 new 创建,就像在类 C 语言中一样。

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;

让我们从地址簿开始。一个姓名列表需要被过滤并添加到搜索索引中。搜索列表的新方法看起来相当令人困惑:普通的 Delphi 开发人员习惯于在 procedure 以上声明所有变量!但得益于 LINQ,存在没有定义类型(即代码中未定义)的列表,甚至不需要声明。

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;

索引搜索器:你的第一个 Web 项目

新构建的搜索索引需要通过 Web 表单进行搜索。创建一个新的 Delphi ASP.NET 项目!同样,Visual Studio 会显示一个设计器窗口。打开代码隐藏文件,会遇到另一个新关键字。partial 将 Delphi 类分割成一个可编辑的文件Default.aspx.pas和设计器生成的Default.aspx.designer.pas文件。当你键入 ASP 代码时,设计器会填充其文件,包含所有必要的声明。你的文件很少会被设计器修改,除非需要你编辑生成的代码。现在,双击 Web 表单上的一个按钮以插入一个事件处理程序。

顺便说一句,这是一个介绍双比较运算符的好地方。你可以使用“0.2 <= score < 0.5”而不是长版本“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;

更多 CPU,请!

性能一直是使用 Delphi 的一个理由。Prism 专注于并行编程,并以节省大量代码和混乱的便捷新关键字取胜。

最方便的关键字是 async,它简单地将一个方法声明为异步。异步方法在单独的线程中执行——自动完成!这样,声明行中的一个单词就可以防止主线程被日志输出调用拖慢。在变量声明中,async 是一个承诺,即该变量将来会包含一个特定值。该值本身在单独的线程中计算。如果另一个线程在计算完成之前访问该变量,该线程将被阻塞,直到该值可用。但最令人印象深刻的消息是异步块。

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

第 04 行何时执行?假设有足够的处理器,它将与第 07 行同时执行。async 使该块在另一个线程中运行。future 变量 myBlock 是该线程的句柄,因此你可以在第 08 行等待它。方法调用 myBlock() 并不执行该块!只有当它尚未完成时,它才会等待线程结束。

在异步块内部,你可以调用其他异步方法。但要小心:主线程不会等待它们。等待一个异步块意味着等待这一个线程,而不是等待该块“嵌套”启动的任何线程。

为了可视化所有这些并行操作,网页包含四个复选框。当用户在浏览器中看到响应页面时,它们将在后台进行评估。

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;

StreamWriter 似乎是用一个空路径初始化的。但是关键字 **future** 导致声明行中的赋值在首次访问对象之前不久执行。此时,路径已分配。方法 WriteLogasync 的,因此它始终在后台运行。

async 块的线程中,一个 for each matching 循环遍历所有控件(复选框)。每次迭代都可以调用另一个异步方法。最终,当主线程等待块完成时,它只等待块的线程,而不等待由块启动的任何附加线程。当用户已经看到完整的响应时,WriteLog 调用仍然可以在后台运行,也许在等待一个空闲的 CPU。

源代码

要编译演示应用程序,你需要程序集 Lucene.Net.dll。如果最新稳定版本不起作用,请尝试版本 2.0.004。

Delphi Prism 的 30 天试用版可从 Embarcadero 下载。遗憾的是,他们不提供免费的Express版本。

© . All rights reserved.