TValue是Delphi的RTTI系统的重要类型。 经过摸索,发现TValue功能强大,可以实现很多功能。本文章中所有程序采用XE3运行通过。

一、TValue结构

TValue定义在System.Rtti.pas

TValue = record

 ...
private
FData: TValueData
end;

TValue提供了一些系列方法,几乎都是操作FData.

TValueData描述如下:

  TValueData = record
FTypeInfo: PTypeInfo;
// FValueData vs old FHeapData:
// FHeapData doubled as storage for interfaces. However, that was ambiguous
// in the case of nil interface values: FTypeInfo couldn't be trusted
// because it looked like the structure was uninitialized. Then, DataSize
// would be .
// FValueData is different: interfaces are always stored like strings etc.,
// as a reference stored in a blob on the heap.
FValueData: IValueData;
case Integer of
: (FAsUByte: Byte);
: (FAsUWord: Word);
: (FAsULong: LongWord);
: (FAsObject: Pointer);
: (FAsClass: TClass);
: (FAsSByte: Shortint);
: (FAsSWord: Smallint);
: (FAsSLong: Longint);
: (FAsSingle: Single);
: (FAsDouble: Double);
: (FAsExtended: Extended);
: (FAsComp: Comp);
: (FAsCurr: Currency);
: (FAsUInt64: UInt64);
: (FAsSInt64: Int64);
: (FAsMethod: TMethod);
: (FAsPointer: Pointer);
end;

TValueData是一个结构体,TValueData可以存储任何类型的数据,经过TValue的方法可以与任何类型进行转换:

TValue = record
...
public
...
// Low-level in
class procedure Make(ABuffer: Pointer; ATypeInfo: PTypeInfo; out Result: TValue); overload; static;
class procedure MakeWithoutCopy(ABuffer: Pointer; ATypeInfo: PTypeInfo; out Result: TValue); overload; static;
class procedure Make(AValue: NativeInt; ATypeInfo: PTypeInfo; out Result: TValue); overload; static; // Low-level out
property DataSize: Integer read GetDataSize;
procedure ExtractRawData(ABuffer: Pointer);
// If internal data is something with lifetime management, this copies a
// reference out *without* updating the reference count.
procedure ExtractRawDataNoCopy(ABuffer: Pointer);
function GetReferenceToRawData: Pointer;
function GetReferenceToRawArrayElement(Index: Integer): Pointer;
...
end;

通过调用Make(...),将任意类型数据转换为TValue
通过调用ExtractRawData(...), ExtractRawDataNoCopy(...)将TValue转换为任意数据类型,两者区别是ExtractRawDataNoCopy转换时在堆中申请内存的数据,而ExtractRawData是安全的。
GetReferenceToRawData返回数据的指针,也是堆内存的指针。

二、类型转换为TValue

下面例子测试Integer和TRect:

 program Project1;
{$APPTYPE CONSOLE}
uses SysUtils, Windows, TypInfo,Rtti; var
IntData : Integer;
IntValue : TValue; RecData : TRect;
RecValue : TValue; begin
IntData := ;
TValue.Make(@IntData,TypeInfo(Integer),IntValue); //Integer类型也可以直接调用 IntValue := IntData; 这里演示TValue.Make
Writeln(IntValue.ToString);
RecData.Left := ;
RecData.Right := ;
TValue.Make(@RecData,TypeInfo(TRect),RecValue);
Writeln(RecValue.ToString);
readln;
end.
运行结果:

(record)

三、TValue转换到类型

在反序列化(反持久化)时,如果知道数据类型,可以调用下面的方法生成一个与此类型相应的TValue空记录:

TValue.Make(nil,TypeInfoVar,OutputTValue);

通过ExtractRawData,可以将TValue数据直接转换某类型数据:

 program Project2;
{$APPTYPE CONSOLE}
uses SysUtils, Windows, TypInfo,Rtti; var
RecData : TRect;
RecDataOut : TRect;
RecValue : TValue; begin
RecData.Left := ;
RecData.Right := ;
TValue.Make(@RecData,TypeInfo(TRect),RecValue); //将TRect结构的RecData转换为TValue类型的RecValue RecValue.ExtractRawData(@RecDataOut); //将TValue 结构数据转换成TRect类型数据
Writeln(RecDataOut.Left);
Writeln(RecDataOut.Right); readln;
end.
运行结果

四、通过TValue的访问类型的成员变量

TValue转换自某个类型后,可以使用的GetReferenceToRawData()获取数据指针,通过调用SetValue和GetValue读写
某个成员的值。

 program Project3;
{$APPTYPE CONSOLE}
uses SysUtils, Windows, TypInfo,Rtti; var
RecData : TRect;
RecValue : TValue;
Ctx : TRttiContext; begin
Ctx := TRttiContext.Create;
// 创建空的、与TRect对应的TValue结构体,
TValue.Make(nil,TypeInfo(TRect),RecValue);
// 设置 Left 、 Right 成员变量值,使用TValue中成员变量的地址指针
Ctx.GetType(TypeInfo(TRect)).GetField('Left').SetValue(RecValue.GetReferenceToRawData,);
Ctx.GetType(TypeInfo(TRect)).GetField('Right').SetValue(RecValue.GetReferenceToRawData,);
// 转换为TRect结构体数据
RecValue.ExtractRawData(@RecData);
Writeln(RecData.Left);
Writeln(RecData.Right);
readln;
Ctx.Free;
end.
运行结果:

五、泛型转换函数

我们上面的例子,通过调用Make函数来转换成TValue,以及通过ExtractRawData转换成需要的类型,
Delphi还提供了泛型转换函数,可以指定已知的类型,直接进行转换:

class function From<T>(const Value: T): TValue; static;
function AsType<T>: T;
function IsType<T>: Boolean;
function TryAsType<T>(out AResult: T): Boolean;
function Cast<T>: TValue; overload;

看下面的例子:

 program Project4;
{$APPTYPE CONSOLE}
uses SysUtils, Windows, TypInfo,Rtti; var
RecData : TRect;
RecDataOut : TRect;
RecValue : TValue;
begin
RecData.Left := ;
RecData.Right := ; RecValue := TValue.From<TRect>(RecData); //直接转换成 TValue Writeln(RecValue.IsType<TRect>); RecDataOut := RecValue.AsType<TRect>; //TValue直接转换成TRect Writeln(RecDataOut.Left);
Writeln(RecDataOut.Right);
readln;
end.
运行结果:

TRUE

六、数组

如果TValue转换自数组类型,则可以调用一下方法:

function GetArrayLength: Integer;
function GetArrayElement(Index: Integer): TValue;
procedure SetArrayElement(Index: Integer; const AValue: TValue);

如果用下面的方式定义数组,则不支持转换到TValue:

var
IntArray : array of Integer;

我可以先定义数组类型后,再定义变量,则可以转换到TValue:

type
TIntArray = array of Integer;
var
IntArray : TIntArray;
// 或者
IntArray : TArray<Integer>; //在 System.pas 定义: TArray<T> = array of T;

七、Variant

Variant与TValue的转换容易产生混淆,调用TValue.FromVariant(),并不是将Varaint转换为TValue:

 program Project5;
{$APPTYPE CONSOLE}
uses SysUtils, TypInfo,Rtti; var
vExample : Variant;
Value : TValue;
begin
vExample := 'Hello World';
Value := TValue.FromVariant(vExample); //
writeln(GetEnumName(TypeInfo(TTypeKind),Ord(Value.Kind)));
Writeln(value.ToString); vExample := ;
Value := TValue.FromVariant(vExample);
writeln(GetEnumName(TypeInfo(TTypeKind),Ord(Value.Kind)));
Writeln(value.ToString); readln;
end.
运行结果:

tkUString
Hello World
tkInteger

如果希望将Variant转换为TValue,可以使用这个方法:

 program Project6;
{$APPTYPE CONSOLE}
uses SysUtils, TypInfo,Rtti; var
vExample : Variant;
Value : TValue;
begin
vExample := 'Hello World';
Value := TValue.From<variant>(vExample);
writeln(GetEnumName(TypeInfo(TTypeKind),Ord(Value.Kind)));
Writeln(value.AsType<variant>); vExample := ;
Value := TValue.From<variant>(vExample);
writeln(GetEnumName(TypeInfo(TTypeKind),Ord(Value.Kind)));
Writeln(value.AsType<variant>); readln;
end.
运行结果:

tkVariant
Hello World
tkVariant

Delphi的TValue探索(一)的更多相关文章

  1. Delphi深度探索-CodeSite应用指南

    Delphi深度探索-CodeSite应用指南 Delphi虽然为我们提供极其强大的调试功能,查找Bug仍然是一项艰巨的工作,通常我们写代码和调试代码的所消耗的时间是大致相同的,甚至有可能更多.为了减 ...

  2. 对delphi中的数据敏感控件的一点探索

    一直对delphi数据敏感控件很好奇,感觉很神奇.只要简单设置一下,就显示和编辑数据,不用写一行代码. 如果不用数据敏感控件,编辑一个表字段数据并保存,我相信应用如下代码. Table1.edit, ...

  3. 探索C#之微型MapReduce

    MapReduce近几年比较热的分布式计算编程模型,以C#为例简单介绍下MapReduce分布式计算. 阅读目录 背景 Map实现 Reduce实现 支持分布式 总结 背景 某平行世界程序猿小张接到B ...

  4. 探索c#之跳跃表(SkipList)

    阅读目录: 基本介绍 算法思想 演化步骤 实现细节 总结 基本介绍 SkipList是William Pugh在1990年提出的,它是一种可替代平衡树的数据结构. SkipList在实现上相对比较简单 ...

  5. Delphi 2009 泛型容器单元(Generics.Collections)[1]: TList<T>

    Delphi 2009 新增了泛型容器单元: Generics.Collections, 同时还有一个 Generics.Defaults 单元做支持. Generics.Collections 包含 ...

  6. Delphi的基于接口(IInterface)的多播监听器模式(观察者模式 )

    本文来自:http://www.cnblogs.com/hezihang/p/6083555.html Delphi采用接口方式设计模块,可以降低模块之间的耦合,便于扩展和维护.本文提供一个实现基于接 ...

  7. delphi 换行操作 Word

    delphi 换行操作 我将我的商用<旅行社管理系统>的 发团通知 部分奉献给您,望对您有所帮助. procedure TFrmMain.N327Click(Sender: TObject ...

  8. delphi 注册表操作(读取、添加、删除、修改)完全手册

    DELPHI VS PASCAL(87)  32位Delphi程序中可利用TRegistry对象来存取注册表文件中的信息. 一.创建和释放TRegistry对象 1.创建TRegistry对象.为了操 ...

  9. Delphi中编辑word

      其他(28)   //启动Word   try     wordapplication1.connect;   except     messagedlg('word may not be ins ...

随机推荐

  1. GIT团队合作探讨之一-保持工作同步的概念和实践

    感谢英文原文作者,这是我看到的关于git协同工作写的最清晰简洁的文章了: https://www.atlassian.com/git/tutorials/syncing/git-push SVN使用一 ...

  2. java操作svn【svnkit】实操

    SVNKit中怎样使用不同的仓库访问协议? 当你下载了最新版的SVNKit二进制文件并且准备使用它时,一个问题出现了,要创建一个库需要做哪些初始化的步骤?直接与Subversion仓库交互已经在低级层 ...

  3. 网络安全-使用HTTP动词篡改的认证旁路

    这个东西去年的安全扫描都没有,今天就扫出来了,非常奇怪的一个东西.好吧,找资料找原因.结果可能应为搜索名词的原因,这个问题在群友的帮助下解决了. 在我理解中servlet只有post和get方法,然后 ...

  4. centos7和centos6区别

    CentOS 7 vs CentOS 6的不同   (1)桌面系统[CentOS6] GNOME 2.x[CentOS7] GNOME 3.x(GNOME Shell) (2)文件系统[CentOS6 ...

  5. bep-10翻译

    dht协议的目的是解放tracter服务器,将tracter的任务分布式存到各个客户端上(即维护资源文件的下载列表,从哪能下载到请求的文件): dht协议在get_peer请求获得peer信息后,就会 ...

  6. 全新Chrome Devtool Performance使用指南

    运行时性能表现(runtime performance)指的是当你的页面在浏览器运行时的性能表现,而不是在下载页面的时候的表现.这篇指南将会告诉你怎么用Chrome DevToos Performan ...

  7. CSAPP Bomb Lab记录

    记录关于CSAPP 二进制炸弹实验过程 (CSAPP配套教学网站Bomb Lab自学版本,实验地址:http://csapp.cs.cmu.edu/2e/labs.html) (个人体验:对x86汇编 ...

  8. ios 线程同步

    https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Multithreading/ThreadSafe ...

  9. Oracle中的rownum不能使用大于>的问题

    标题:Oracle中的rownum不能使用大于>的问题 一.对rownum的说明 关于Oracle 的 rownum 问题,很多资料都说不支持SQL语句中的“>.>=.=.betwe ...

  10. BZOJ1037:[ZJOI2008]生日聚会Party(DP)

    Description 今天是hidadz小朋友的生日,她邀请了许多朋友来参加她的生日party.hidadz带着朋友们来到花园中,打算坐成一排玩游戏. 为了游戏不至于无聊,就座的方案应满足如下条件: ...