在Nullable< T>中设置值.用RTTI记录 [英] Setting a value in a Nullable<T> record with RTTI

查看:49
本文介绍了在Nullable< T>中设置值.用RTTI记录的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用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&lt; T&gt;中设置值.用RTTI记录的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆