Delphi.NET 内部实现分析(5)
来源:百度文库 编辑:神马文学网 时间:2024/04/28 13:07:09
2.5 其它
在了解了Borland.Delphi.System中的几个重要部分之后,剩下的就是一些零零碎碎的扫尾工作。
2.5.1 类型别名
为兼容Delphi中的特有类型,Borland.Delphi.System单元中定义了很多类型别名。
如我们前面分析过的TObject就是System.Object的别名。
//-----------------------------------------Borland.Delphi.System.pas--
type
TDateTime = type Double;
Extended = type Double; // 80 bit reals are unique to the Intel x86 architecture
Comp = Int64 deprecated;
TGUID = packed record
D1: LongWord;
D2: Word;
D3: Word;
D4: array[0..7] of Byte;
end;
//-----------------------------------------Borland.Delphi.System.pas--
对于Delphi的TDateTime类型来说,它在实现上是以一个Double即8字节浮点数存储的,
兼容OLE自动化中的时间格式。在Delphi.NET中继承了这一存储方式,而没有直接使用BCL
提供的System.DateTime结构,不过仍然可以使用DateTime.FromOADate和
DateTime.ToOADate方法在System.DateTime和TDateTime之间双向转换。
格式存储说明如下(from MSDN)
OLE 自动化日期以浮点数形式实现,其值为距 1899 年 12 月 30 日午夜的天数。
例如,1899 年 12 月 31 日午夜表示为 1.0;1900 年 1 月 1 日早晨 6 点表示为 2.25;
1899 年 12 月 29 日午夜表示为 -1.0;1899 年 12 月 29 日早晨 6 点表示为 -1.25。
只有刻度值大于或等于正的或负的 31241376000000000 的 DateTime 对象才可以表示为
OLE 自动化日期。未初始化的 DateTime(即刻度值为 0 的实例)将转换为等效的未初始化
OLE 自动化日期(即值为 0.0 的日期,它表示 1899 年 12 月 30 日午夜)。
而Extended和Comp则只是一个简单的别名。TGUID也只是一个简单的重定义。
值得注意的是这里的packed关键字。在CLR中,类的成员的物理位置对程序本身是没有意义的,
CLR可以任意安排字段的位置以进行字节对齐等等优化操作。而为了与现有代码进行交互,CLR提供了
StructLayoutAttribute属性允许限定类型的内部物理结构。在Delphi.NET中可以通过packed
关键字定义此结构的成员必须按定义的次序在内存中排列,即LayoutKind.Sequential的形式。
而在Delphi.NET中,所有的record在实现上都是ValueType的子类,即值类型,直接在堆栈上操作。
2.5.2 异常
同TObject一样,Delphi中异常类继承链的根Exception在Delphi.NET中,也只是BCL的异常类
System.Exception的一个别名,而只是通过class helper提供源代码级兼容性。
//-----------------------------------------Borland.Delphi.System.pas--
Exception = System.Exception;
ExceptionHelper = class helper for Exception
private
class function CreateMsg(const Msg: string): Exception;
function GetHelpContext: Integer;
procedure SetHelpContext(AHelpContext: Integer);
public
// Doc: The help context return zero(0) if exception‘s helplink property
// cannot be parsed into an integer.
property HelpContext: Integer read GetHelpContext write SetHelpContext;
// constructor Create(const Msg: string) is provided by the CLR class
class function CreateFmt(const Msg: string; const Args: array of const): Exception;
class function CreateHelp(const Msg: string; AHelpContext: Integer): Exception;
class function CreateFmtHelp(const Msg: string; const Args: array of const;
AHelpContext: Integer): Exception;
end;
ExceptionClass = class of Exception;
EConvertError = class(Exception);
threadvar
_ExceptObject: TObject;
function ExceptObject: TObject;
//-----------------------------------------Borland.Delphi.System.pas--
几个类函数Create*(...)都只是对System.Exception构造函数的包装而已,
用于保存异常相关帮助文件路径的System.Exception.HelpLink属性,则被Delphi.NET
用于保存HelpContext,因为在VCL中,所有的异常都是共用一个帮助文件TApplication.HelpFile。
而ExceptObject函数则由编译器支持,提供访问当前被抛出的异常的手段。此函数在Delphi中
通过VCL维护的SEH异常链获取,而在Delphi.NET中只好由编译器在异常被截获后手动赋值给线程局部存储变量
_ExceptObject,然后再由ExceptObject函数读出,这只是语法一级兼容Delphi而已。
不过这个预览版好像没有提供threadvar的支持,只是把它简单的放到Borland.Delphi.System单元的
全局变量中,作为自动生成Unit类的一个静态成员变量而已,并非线程安全!在Borland.Delphi.Classes
中甚至直接把TThread的定义注释掉,实现不提供。估计还在开发中……sigh
2.5.3 断言(Assert)
断言负责在调试模式下检测一个条件是否成立,失败则引发异常。
//-----------------------------------------Borland.Delphi.System.pas--
interface
{ debugging functions }
procedure _Assert(const Message, Filename: AnsiString; LineNumber: Integer);
type
EAssertionFailed = class(Exception)
public
ShortMessage: string;
Filename: string;
LineNumber: Integer;
end;
resourcestring
SAssertionFailed = ‘%s (%s at %d)‘;
implementation
procedure _Assert(const Message, Filename: AnsiString; LineNumber: Integer);
var
LException: EAssertionFailed;
begin
{ TODO : Should we be using System.Diagnostics.Debug.Assert/Fail? }
{$MESSAGE WARN ‘Assert doesn‘‘t use CreateFmt because it returns the wrong type‘}
LException := EAssertionFailed.Create(Format(SAssertionFailed, [Message, Filename, LineNumber]));
LException.ShortMessage := Message;
LException.Filename := Filename;
LException.LineNumber := LineNumber;
raise LException;
end;
//-----------------------------------------Borland.Delphi.System.pas--
_Assert函数的定义基本上是EAssertionFailed异常的一个简单包装。因为Delphi没有提供类似
C++中__FILE__、__LINE__之类的预定义宏,故而只能由编译器在用户使用到Assert函数时,
将当前文件名、行号等调试信息编译进代码中,即在编译器一级提供断言实现。
//-----------------------------------------Borland.Delphi.System.pas--
function Assigned(const AGCHandle: GCHandle): boolean;
begin
Result := AGCHandle.IsAllocated;
end;
//-----------------------------------------Borland.Delphi.System.pas--
//-----------------------------------------GCHandle.cs--
namespace System.Runtime.InteropServices {
public struct GCHandle {
private IntPtr m_handle;
public bool IsAllocated { get { return m_handle != IntPtr.Zero; } }
}
}
//-----------------------------------------GCHandle.cs--
Assigned则是对一个引用类型变量进行检测,与Delphi类似,Delphi.NET中直接通过检测引用类型值
是否为空(null)判断是否有效,但对于值类型则将之与0进行比较。
2.5.4 随机数
Delphi.NET中的随机数基本上是对BCL相关类的一个简单包装,而BCL的随机数算法与VCL一样弱智,
简单的功能还凑合,BCL的System.Security.Cryptography.RandomNumberGenerator相比之下
随机性就好得多,不过要付出速度上的代价。
//-----------------------------------------Borland.Delphi.System.pas--
interface
{ random functions }
var
RandSeed: LongInt = 0;
procedure Randomize;
function Random(const ARange: Integer): Integer; overload;
function Random: Extended; overload;
implementation
var
LastRandSeed: Integer = -1;
RandomEngine: System.Random;
procedure InitRandom;
begin
if LastRandSeed <> RandSeed then
begin
if RandSeed = 0 then
RandomEngine := System.Random.Create
else
RandomEngine := System.Random.Create(RandSeed);
LastRandSeed := RandSeed;
end;
end;
procedure Randomize;
begin
LastRandSeed := -1;
RandSeed := 0;
end;
function Random(const ARange: Integer): Integer;
begin
InitRandom;
Result := RandomEngine.Next(ARange);
end;
function Random: Extended;
begin
InitRandom;
Result := RandomEngine.NextDouble;
end;
//-----------------------------------------Borland.Delphi.System.pas—
Delphi.NET 内部实现分析(6)
不好意思,实在想不到有什么值得说的了,只好草草结束了
2.5.5 其它.其它
Borland.Delphi.System单元虽然比Delphi中的System单元小的多,
但其中也充斥着大量常用但是实现代码枯燥的函数。如
数字处理函数集
字符串处理函数集
命令行信息获取函数集(CmdLine/ParamCount/ParamStr)
格式化输出函数集(Format等)
文本文件(即Text类型,而File类型文件不提供支持)开/关/读/写等函数集
动态数组管理(System.Array类型的简单包装)
当前路径及目录操作函数集
集合类型(CLR中并无集合概念,Set实现上是字节数组的简单包装)
其它一些杂项函数
等等等等
这些零散代码基本上都是对BCL相应功能的简单包装,这里就不一一详述了。
2.5.6 小结
至此,对Delphi.NET中核心单元Borland.Delphi.System单元的介绍
就告一段落了。通过对此单元的分析,我们大致了解了Delphi.NET中对于Delphi
一些核心概念的实现或模仿思路,但不排除在正式版中实现有所改变。
题外话:
首先感谢大家的热心支持,这是督促我这个懒人写完文章(哪怕是草草结束)的最大动力,
也希望这篇文章能够对大家了解即将到来的Delphi.NET、迎接.NET时代有所帮助。
这个系列文章到这里估计也就暂时告一段落了,因为时间仓促、准备不足而且
笔者水平有限,只涉及到Delphi.NET在实现上与Delphi不同的部分内容,
与Delphi.NET的改变来说只是冰山一角而已。本来还想扩大一点分析面,
但考虑到Delphi.NET中RTL其它单元大多只是对原有Delphi代码的BCL封装移植
技术难度并不大,对Delphi熟悉的读者直接阅读源程序可能比看我的文章更容易一些。
因此在分析完涉及到一些底层只是的Borland.Delphi.System后就此打住,
虽然有些虎头蛇尾之嫌,但总免得背画蛇添足之骂名 :)
至于构建在Delphi.NET的RLT之上的应用层架构VCL和以后可能要支持的CLX,
我就没有太多精力写文章介绍了。因为就目前实现的VCL代码来看,只是将以前的VCL代码
managed化而已,实现上还是使用Windows那套传统API管理窗口,与BCL的
System.Windows.Forms.Form根本不搭界。这样一来在Delphi.NET中又多了一个选择
VCL or CLX or System.Windows.Forms.Form...sigh,是好是坏只能待时间评判。
文中如果有解释不够清楚的地方,大家可以跟贴提出。也欢迎来信
于我讨论Delphi.NET和CLR相关问题。 再次感谢大家的支持!:)
在了解了Borland.Delphi.System中的几个重要部分之后,剩下的就是一些零零碎碎的扫尾工作。
2.5.1 类型别名
为兼容Delphi中的特有类型,Borland.Delphi.System单元中定义了很多类型别名。
如我们前面分析过的TObject就是System.Object的别名。
//-----------------------------------------Borland.Delphi.System.pas--
type
TDateTime = type Double;
Extended = type Double; // 80 bit reals are unique to the Intel x86 architecture
Comp = Int64 deprecated;
TGUID = packed record
D1: LongWord;
D2: Word;
D3: Word;
D4: array[0..7] of Byte;
end;
//-----------------------------------------Borland.Delphi.System.pas--
对于Delphi的TDateTime类型来说,它在实现上是以一个Double即8字节浮点数存储的,
兼容OLE自动化中的时间格式。在Delphi.NET中继承了这一存储方式,而没有直接使用BCL
提供的System.DateTime结构,不过仍然可以使用DateTime.FromOADate和
DateTime.ToOADate方法在System.DateTime和TDateTime之间双向转换。
格式存储说明如下(from MSDN)
OLE 自动化日期以浮点数形式实现,其值为距 1899 年 12 月 30 日午夜的天数。
例如,1899 年 12 月 31 日午夜表示为 1.0;1900 年 1 月 1 日早晨 6 点表示为 2.25;
1899 年 12 月 29 日午夜表示为 -1.0;1899 年 12 月 29 日早晨 6 点表示为 -1.25。
只有刻度值大于或等于正的或负的 31241376000000000 的 DateTime 对象才可以表示为
OLE 自动化日期。未初始化的 DateTime(即刻度值为 0 的实例)将转换为等效的未初始化
OLE 自动化日期(即值为 0.0 的日期,它表示 1899 年 12 月 30 日午夜)。
而Extended和Comp则只是一个简单的别名。TGUID也只是一个简单的重定义。
值得注意的是这里的packed关键字。在CLR中,类的成员的物理位置对程序本身是没有意义的,
CLR可以任意安排字段的位置以进行字节对齐等等优化操作。而为了与现有代码进行交互,CLR提供了
StructLayoutAttribute属性允许限定类型的内部物理结构。在Delphi.NET中可以通过packed
关键字定义此结构的成员必须按定义的次序在内存中排列,即LayoutKind.Sequential的形式。
而在Delphi.NET中,所有的record在实现上都是ValueType的子类,即值类型,直接在堆栈上操作。
2.5.2 异常
同TObject一样,Delphi中异常类继承链的根Exception在Delphi.NET中,也只是BCL的异常类
System.Exception的一个别名,而只是通过class helper提供源代码级兼容性。
//-----------------------------------------Borland.Delphi.System.pas--
Exception = System.Exception;
ExceptionHelper = class helper for Exception
private
class function CreateMsg(const Msg: string): Exception;
function GetHelpContext: Integer;
procedure SetHelpContext(AHelpContext: Integer);
public
// Doc: The help context return zero(0) if exception‘s helplink property
// cannot be parsed into an integer.
property HelpContext: Integer read GetHelpContext write SetHelpContext;
// constructor Create(const Msg: string) is provided by the CLR class
class function CreateFmt(const Msg: string; const Args: array of const): Exception;
class function CreateHelp(const Msg: string; AHelpContext: Integer): Exception;
class function CreateFmtHelp(const Msg: string; const Args: array of const;
AHelpContext: Integer): Exception;
end;
ExceptionClass = class of Exception;
EConvertError = class(Exception);
threadvar
_ExceptObject: TObject;
function ExceptObject: TObject;
//-----------------------------------------Borland.Delphi.System.pas--
几个类函数Create*(...)都只是对System.Exception构造函数的包装而已,
用于保存异常相关帮助文件路径的System.Exception.HelpLink属性,则被Delphi.NET
用于保存HelpContext,因为在VCL中,所有的异常都是共用一个帮助文件TApplication.HelpFile。
而ExceptObject函数则由编译器支持,提供访问当前被抛出的异常的手段。此函数在Delphi中
通过VCL维护的SEH异常链获取,而在Delphi.NET中只好由编译器在异常被截获后手动赋值给线程局部存储变量
_ExceptObject,然后再由ExceptObject函数读出,这只是语法一级兼容Delphi而已。
不过这个预览版好像没有提供threadvar的支持,只是把它简单的放到Borland.Delphi.System单元的
全局变量中,作为自动生成Unit类的一个静态成员变量而已,并非线程安全!在Borland.Delphi.Classes
中甚至直接把TThread的定义注释掉,实现不提供。估计还在开发中……sigh
2.5.3 断言(Assert)
断言负责在调试模式下检测一个条件是否成立,失败则引发异常。
//-----------------------------------------Borland.Delphi.System.pas--
interface
{ debugging functions }
procedure _Assert(const Message, Filename: AnsiString; LineNumber: Integer);
type
EAssertionFailed = class(Exception)
public
ShortMessage: string;
Filename: string;
LineNumber: Integer;
end;
resourcestring
SAssertionFailed = ‘%s (%s at %d)‘;
implementation
procedure _Assert(const Message, Filename: AnsiString; LineNumber: Integer);
var
LException: EAssertionFailed;
begin
{ TODO : Should we be using System.Diagnostics.Debug.Assert/Fail? }
{$MESSAGE WARN ‘Assert doesn‘‘t use CreateFmt because it returns the wrong type‘}
LException := EAssertionFailed.Create(Format(SAssertionFailed, [Message, Filename, LineNumber]));
LException.ShortMessage := Message;
LException.Filename := Filename;
LException.LineNumber := LineNumber;
raise LException;
end;
//-----------------------------------------Borland.Delphi.System.pas--
_Assert函数的定义基本上是EAssertionFailed异常的一个简单包装。因为Delphi没有提供类似
C++中__FILE__、__LINE__之类的预定义宏,故而只能由编译器在用户使用到Assert函数时,
将当前文件名、行号等调试信息编译进代码中,即在编译器一级提供断言实现。
//-----------------------------------------Borland.Delphi.System.pas--
function Assigned(const AGCHandle: GCHandle): boolean;
begin
Result := AGCHandle.IsAllocated;
end;
//-----------------------------------------Borland.Delphi.System.pas--
//-----------------------------------------GCHandle.cs--
namespace System.Runtime.InteropServices {
public struct GCHandle {
private IntPtr m_handle;
public bool IsAllocated { get { return m_handle != IntPtr.Zero; } }
}
}
//-----------------------------------------GCHandle.cs--
Assigned则是对一个引用类型变量进行检测,与Delphi类似,Delphi.NET中直接通过检测引用类型值
是否为空(null)判断是否有效,但对于值类型则将之与0进行比较。
2.5.4 随机数
Delphi.NET中的随机数基本上是对BCL相关类的一个简单包装,而BCL的随机数算法与VCL一样弱智,
简单的功能还凑合,BCL的System.Security.Cryptography.RandomNumberGenerator相比之下
随机性就好得多,不过要付出速度上的代价。
//-----------------------------------------Borland.Delphi.System.pas--
interface
{ random functions }
var
RandSeed: LongInt = 0;
procedure Randomize;
function Random(const ARange: Integer): Integer; overload;
function Random: Extended; overload;
implementation
var
LastRandSeed: Integer = -1;
RandomEngine: System.Random;
procedure InitRandom;
begin
if LastRandSeed <> RandSeed then
begin
if RandSeed = 0 then
RandomEngine := System.Random.Create
else
RandomEngine := System.Random.Create(RandSeed);
LastRandSeed := RandSeed;
end;
end;
procedure Randomize;
begin
LastRandSeed := -1;
RandSeed := 0;
end;
function Random(const ARange: Integer): Integer;
begin
InitRandom;
Result := RandomEngine.Next(ARange);
end;
function Random: Extended;
begin
InitRandom;
Result := RandomEngine.NextDouble;
end;
//-----------------------------------------Borland.Delphi.System.pas—
Delphi.NET 内部实现分析(6)
不好意思,实在想不到有什么值得说的了,只好草草结束了
2.5.5 其它.其它
Borland.Delphi.System单元虽然比Delphi中的System单元小的多,
但其中也充斥着大量常用但是实现代码枯燥的函数。如
数字处理函数集
字符串处理函数集
命令行信息获取函数集(CmdLine/ParamCount/ParamStr)
格式化输出函数集(Format等)
文本文件(即Text类型,而File类型文件不提供支持)开/关/读/写等函数集
动态数组管理(System.Array类型的简单包装)
当前路径及目录操作函数集
集合类型(CLR中并无集合概念,Set实现上是字节数组的简单包装)
其它一些杂项函数
等等等等
这些零散代码基本上都是对BCL相应功能的简单包装,这里就不一一详述了。
2.5.6 小结
至此,对Delphi.NET中核心单元Borland.Delphi.System单元的介绍
就告一段落了。通过对此单元的分析,我们大致了解了Delphi.NET中对于Delphi
一些核心概念的实现或模仿思路,但不排除在正式版中实现有所改变。
题外话:
首先感谢大家的热心支持,这是督促我这个懒人写完文章(哪怕是草草结束)的最大动力,
也希望这篇文章能够对大家了解即将到来的Delphi.NET、迎接.NET时代有所帮助。
这个系列文章到这里估计也就暂时告一段落了,因为时间仓促、准备不足而且
笔者水平有限,只涉及到Delphi.NET在实现上与Delphi不同的部分内容,
与Delphi.NET的改变来说只是冰山一角而已。本来还想扩大一点分析面,
但考虑到Delphi.NET中RTL其它单元大多只是对原有Delphi代码的BCL封装移植
技术难度并不大,对Delphi熟悉的读者直接阅读源程序可能比看我的文章更容易一些。
因此在分析完涉及到一些底层只是的Borland.Delphi.System后就此打住,
虽然有些虎头蛇尾之嫌,但总免得背画蛇添足之骂名 :)
至于构建在Delphi.NET的RLT之上的应用层架构VCL和以后可能要支持的CLX,
我就没有太多精力写文章介绍了。因为就目前实现的VCL代码来看,只是将以前的VCL代码
managed化而已,实现上还是使用Windows那套传统API管理窗口,与BCL的
System.Windows.Forms.Form根本不搭界。这样一来在Delphi.NET中又多了一个选择
VCL or CLX or System.Windows.Forms.Form...sigh,是好是坏只能待时间评判。
文中如果有解释不够清楚的地方,大家可以跟贴提出。也欢迎来信
于我讨论Delphi.NET和CLR相关问题。 再次感谢大家的支持!:)
Delphi.NET 内部实现分析(5)
Delphi.NET 内部实现分析(5)
Delphi.NET 内部实现分析(1)
Delphi.NET 内部实现分析(2)
Delphi.NET 内部实现分析(3.1)
Delphi.NET 内部实现分析(3.2)
Delphi.NET 内部实现分析(3.3)
Delphi.NET 内部实现分析(3.4)
Delphi.NET 内部实现分析(4)
Delphi.NET 内部实现分析(4)
Delphi.NET 内部实现分析(3.4)
PAM 的应用开发和内部实现源码分析
Delphi中PING的实现
Delphi和Java实现webservice
用DELPHI实现为NT添加用户
Delphi中PING的实现ewe
Delphi中PING的实现a
Delphi屏幕截图技术实现2
Delphi屏幕截图技术实现d
使用.NET实现断点续传
在Delphi中实现VFP6的查询速度 - Delphi - 文档中心 - 源码天空
浅析多层结构及其在Delphi中的实现<一>
Delphi中不使用第三方控件实现XP风格
DELPHI的RTTI实现数据集的简单对象化