Delphi的TValue探索(一)
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
最新文章
- windows耳机没有声音
- 用python DIY一个图片转pdf工具并打包成exe
- (转)扩展jquery easyui datagrid 之动态绑定列和数据
- Android 框架简介--Java环境(转)
- PNG文件结构分析 ---Png解析
- android 访问SMS短信收件箱
- 关于ArrayList线程安全解决方案
- 武汉科技大学ACM :1008: 华科版C语言程序设计教程(第二版)习题6.14
- PHP5.5在windows 安装使用 memcached 服务端的方法以及 php_memcache.dll 下载
- ajax请求后台,有时收不到返回值的解决办法
- Python 操作 GA API 指南
- Azure Automation (6) 执行Azure SQL Job
- MySQL千万级数据库查询怎么提高查询效率
- 简单的可以跑起来的dubbo例子
- css变换与动画详解
- 2017-2018-2 1723《程序设计与数据结构》第九周作业 &; 第二周结对编程 总结
- PYTHON-网络通信 TCP
- Cloud Foundry 组件
- node vue 开发环境部署时,外部访问页面出现: Invalid Host header 服务器域名访问出现的问题
- Linux操作系统CentOS7.2发行版本的安装与配置(安装是选择服务器类型)