问题描述
我正在使用Paolo Rossi的 NEON 库进行序列化/反序列化.
I'm working with Serialization/Deserialization using NEON library from Paolo Rossi.
我正在尝试使用RTTI以及从数据库中获取的数据来填充此类.该类上的属性与数据库中的字段具有相同的名称.
i'm trying to populate this class using RTTI, with data that i get from a database.The properties on the class have the same name of the fields in the database.
在图书馆里,我有这个可空记录:
In the library, i have this Nullable Record :
unit Neon.Core.Nullables;
interface
uses
System.SysUtils, System.Variants, System.Classes, System.Generics.Defaults, System.Rtti,
System.TypInfo, System.JSON;
type
ENullableException = class(Exception);
{$RTTI EXPLICIT FIELDS([vcPrivate]) METHODS([vcPrivate])}
Nullable<T> = record
private
FValue: T;
FHasValue: string;
procedure Clear;
function GetValueType: PTypeInfo;
function GetValue: T;
procedure SetValue(const AValue: T);
function GetHasValue: Boolean;
public
constructor Create(const Value: T); overload;
constructor Create(const Value: Variant); overload;
function Equals(const Value: Nullable<T>): Boolean;
function GetValueOrDefault: T; overload;
function GetValueOrDefault(const Default: T): T; overload;
property HasValue: Boolean read GetHasValue;
function IsNull: Boolean;
property Value: T read GetValue;
class operator Implicit(const Value: Nullable<T>): T;
class operator Implicit(const Value: Nullable<T>): Variant;
class operator Implicit(const Value: Pointer): Nullable<T>;
class operator Implicit(const Value: T): Nullable<T>;
class operator Implicit(const Value: Variant): Nullable<T>;
class operator Equal(const Left, Right: Nullable<T>): Boolean;
class operator NotEqual(const Left, Right: Nullable<T>): Boolean;
end;
NullString = Nullable<string>;
NullBoolean = Nullable<Boolean>;
NullInteger = Nullable<Integer>;
NullInt64 = Nullable<Int64>;
NullDouble = Nullable<Double>;
NullDateTime = Nullable<TDateTime>;
implementation
uses
Neon.Core.Utils;
{ Nullable<T> }
constructor Nullable<T>.Create(const Value: T);
var
a: TValue;
begin
FValue := Value;
FHasValue := DefaultTrueBoolStr;
end;
constructor Nullable<T>.Create(const Value: Variant);
begin
if not VarIsNull(Value) and not VarIsEmpty(Value) then
Create(TValue.FromVariant(Value).AsType<T>)
else
Clear;
end;
procedure Nullable<T>.Clear;
begin
FValue := Default(T);
FHasValue := '';
end;
function Nullable<T>.Equals(const Value: Nullable<T>): Boolean;
begin
if HasValue and Value.HasValue then
Result := TEqualityComparer<T>.Default.Equals(Self.Value, Value.Value)
else
Result := HasValue = Value.HasValue;
end;
function Nullable<T>.GetHasValue: Boolean;
begin
Result := FHasValue <> '';
end;
function Nullable<T>.GetValueType: PTypeInfo;
begin
Result := TypeInfo(T);
end;
function Nullable<T>.GetValue: T;
begin
if not HasValue then
raise ENullableException.Create('Nullable type has no value');
Result := FValue;
end;
function Nullable<T>.GetValueOrDefault(const Default: T): T;
begin
if HasValue then
Result := FValue
else
Result := Default;
end;
function Nullable<T>.GetValueOrDefault: T;
begin
Result := GetValueOrDefault(Default(T));
end;
class operator Nullable<T>.Implicit(const Value: Nullable<T>): T;
begin
Result := Value.Value;
end;
class operator Nullable<T>.Implicit(const Value: Nullable<T>): Variant;
begin
if Value.HasValue then
Result := TValue.From<T>(Value.Value).AsVariant
else
Result := Null;
end;
class operator Nullable<T>.Implicit(const Value: Pointer): Nullable<T>;
begin
if Value = nil then
Result.Clear
else
Result := Nullable<T>.Create(T(Value^));
end;
class operator Nullable<T>.Implicit(const Value: T): Nullable<T>;
begin
Result := Nullable<T>.Create(Value);
end;
class operator Nullable<T>.Implicit(const Value: Variant): Nullable<T>;
begin
Result := Nullable<T>.Create(Value);
end;
function Nullable<T>.IsNull: Boolean;
begin
Result := FHasValue = '';
end;
class operator Nullable<T>.Equal(const Left, Right: Nullable<T>): Boolean;
begin
Result := Left.Equals(Right);
end;
class operator Nullable<T>.NotEqual(const Left, Right: Nullable<T>): Boolean;
begin
Result := not Left.Equals(Right);
end;
procedure Nullable<T>.SetValue(const AValue: T);
begin
FValue := AValue;
FHasValue := DefaultTrueBoolStr;
end;
end.
这是模型类:
type
TMyClass = class(TPersistent)
private
FMyIntegerProp: Nullable<Integer>;
procedure SetMyIntegerProp(const Value: Nullable<Integer>);
published
Property MyIntegerProp: Nullable<Integer> read FMyIntegerProp write SetMyIntegerProp;
end;
implementation
{ TMyClass }
procedure TMyClass.SetMyIntegerProp(const Value: Nullable<Integer>);
begin
FMyIntegerProp := Value;
end;
到目前为止,我的代码是
And my code so far :
procedure DatasetToObject(AObject: TObject; AQuery: TFDQuery);
var
n: Integer;
LRttiContext: TRttiContext;
LRttiType: TRttiType;
LRttiProperty: TRttiProperty;
LFieldName: string;
Value: TValue;
LValue: TValue;
LRttiMethod : TRttiMethod;
begin
LRttiContext := TRttiContext.Create;
try
LRttiType := LRttiContext.GetType(AObject.ClassType);
for n := 0 to AQuery.FieldCount - 1 do
begin
LRttiProperty := LRttiType.GetProperty(AQuery.Fields[n].FieldName);
if (LRttiProperty <> nil) and (LRttiProperty.PropertyType.TypeKind = tkRecord) then
begin
LValue := LRttiProperty.GetValue(AObject);
LRttiMethod := LRttiContext.GetType(LValue.TypeInfo).GetMethod('SetValue');
if (LRttiProperty.PropertyType.Name = 'Nullable<System.Integer>') then
LRttiMethod.Invoke(LValue, [AQuery.Fields[n].AsInteger]).AsInteger;
end;
end;
finally
LRttiContext.Free;
end;
end;
但到目前为止没有成功,将不胜感激.
but no success so far, any help will be appreciated.
推荐答案
Nullable.SetValue()
没有返回值,但是您尝试在调用 AsInteger时读取一个值
在 TRttiMethod.Invoke()
返回的 TValue
上.这将导致在运行时引发异常.
Nullable.SetValue()
does not have a return value, but you are trying to read one when you call AsInteger
on the TValue
that TRttiMethod.Invoke()
returns. That will cause an exception to be raised at runtime.
此外,当您读取 TMyClass.MyIntegerProp
属性的值时,最终将得到其 Nullable
记录的 copy ,因此该副本上的 Invoke()
'ing SetValue()
不会更新 MyIntegerProp
属性.之后,您必须将修改后的 Nullable
分配回 MyIntegerProp
,例如:
Also, when you read the value of the TMyClass.MyIntegerProp
property, you will end up with a copy of its Nullable
record, so Invoke()
'ing SetValue()
on that copy is not going to update the MyIntegerProp
property. You will have to assign the modified Nullable
back to MyIntegerProp
afterwards, eg:
LValue := LRttiProperty.GetValue(AObject);
LRttiMethod := LRttiContext.GetType(LValue.TypeInfo).GetMethod('SetValue');
LRttiMethod.Invoke(LValue, [AQuery.Fields[n].AsInteger]);
LRttiProperty.SetValue(AObject, LValue); // <-- add this!
话虽这么说, Nullable.Value
属性是只读的,但是 Nullable
有一个 SetValue()
方法,所以我会建议将 Value
属性更改为可读写,例如:
That being said, the Nullable.Value
property is read-only, but Nullable
has a SetValue()
method, so I would suggest changing the Value
property to be read-write instead, eg:
property Value: T read GetValue write SetValue;
然后,您可以通过RTTI设置 Value
属性,而不用直接通过 SetValue()
方法设置 Invoke()
:
Then you can set the Value
property via RTTI instead of Invoke()
'ing the SetValue()
method directly:
var
...
//LRttiMethod: TRttiMethod;
LRttiValueProp: TRttiProperty;
...
...
LRttiProperty := LRttiType.GetProperty(AQuery.Fields[n].FieldName);
if (LRttiProperty <> nil) and
(LRttiProperty.PropertyType.TypeKind = tkRecord) and
(LRttiProperty.PropertyType.Name = 'Nullable<System.Integer>') then
begin
LValue := LRttiProperty.GetValue(AObject);
{
LRttiMethod := LRttiContext.GetType(LValue.TypeInfo).GetMethod('SetValue');
LRttiMethod.Invoke(LValue, [AQuery.Fields[n].AsInteger]);
}
LRttiValueProp := LRttiContext.GetType(LValue.TypeInfo).GetProperty('Value');
LRttiValueProp.SetValue(LValue.GetReferenceToRawData, AQuery.Fields[n].AsInteger);
LRttiProperty.SetValue(AObject, LValue);
end;
这篇关于在Nullable< T>中设置值.用RTTI记录的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!