Delphi Prism 入门






4.91/5 (12投票s)
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
。在类定义中,你会发现新的关键字 partial
和 method
。后者取代了 procedure
和 function
,因为 .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
** 导致声明行中的赋值在首次访问对象之前不久执行。此时,路径已分配。方法 WriteLog
是 async
的,因此它始终在后台运行。
在 async
块的线程中,一个 for each matching
循环遍历所有控件(复选框)。每次迭代都可以调用另一个异步方法。最终,当主线程等待块完成时,它只等待块的线程,而不等待由块启动的任何附加线程。当用户已经看到完整的响应时,WriteLog
调用仍然可以在后台运行,也许在等待一个空闲的 CPU。
源代码
要编译演示应用程序,你需要程序集 Lucene.Net.dll。如果最新稳定版本不起作用,请尝试版本 2.0.004。
Delphi Prism 的 30 天试用版可从 Embarcadero 下载。遗憾的是,他们不提供免费的Express版本。