3

在使用默认内置“组件流系统”的特定方式中,我发现如果属性的值等于默认值,则不写入它是有问题的。

让我们考虑以下方案:

您使用WriteComponent()ReadComponent()来保存组件的特定状态。我们称这种状态为预设。该组件包含各种带有 setter 的 Real-typed 属性。

我们知道,如果一个属性等于它的默认值,那么预设将不包含该值。

所以对于我们的组件

  1. 我们将属性AFLoat设置为0.0
  2. 我们将预设保存在流中 (MyStream.WriteComponent(MyInstance))
  3. 我们将属性 AFLoat设置为0.101
  4. 我们重新加载预设(MyStream.ReadComponent(MyInstance))

最后在重新加载预设后,AFLoat仍然等于0.101,而我们期望它的值为0.0

该错误的根源很明显,属性的默认值从未写入组件流中。所以在第2步中:该属性没有被写入,然后在第 4 步中它没有被读取......不是很烦人!

有没有办法强制将属性的默认值写入组件流中?实际上,我对 Real-typed 属性有一个自制的修复程序,但我想知道是否有一种众所周知的方法来克服潜在的问题。

我的自定义修复是在调用 ReadComponent ()之前使用 TypInfos 将 Real-typed 属性重置为 0

Procedure ResetFloatToNull(Const AnObject: TObject; Recursive: Boolean);
Var
  i,j: Integer;
  LList: PPropList;
Begin
  j := GetPropList( AnObject, LList);
  If j > 0 Then For i:= 0 To j-1 Do
    Case LList^[i].PropType^.Kind Of
    // floats with the object scope
    tkFloat: SetFloatProp(AnObject,LList^[i].Name,0);
    // floats with a subobject scope (IsSubComponent)
    tkClass: If Recursive Then
      ResetFloatToNull( TObject(GetOrdProp(AnObject,LList^[i])), True);
    End;
  FreeMem(LList);
End;

但是如果不存在其他方法,那么(隐含和次要问题):EMB 不应该放弃默认值吗?虽然它对 IDE 对象检查器(在上下文菜单中重置为继承)有点兴趣,但它完全导致了组件序列化系统中的各种烦恼......

我希望你能得到基本的问题,否则我可以添加一个小例子......


错误的小演示(在 DH 的第一个答案和评论战之后添加):

program Project1;
{$APPTYPE CONSOLE}
uses
  SysUtils,
  classes;

type
  TTestClass = class(TComponent)
  private
    ffloat1,ffloat2: single;
  published
    property float1: single read ffloat1 write ffloat1;
    property float2: single read ffloat2 write ffloat2;
  end;

var
  str: TMemoryStream;
  testclass: TTestClass;

begin
  testclass := TTestClass.Create(Nil);
  str := TMemoryStream.Create;
  //
  testclass.float1 := 0.31;
  testclass.float2 := 0.32;
  //
  testclass.float1 := 0.0;
  testclass.float2 := 0.2;
  str.WriteComponent(testclass);
  writeln( 'we have wrote a state when the prop 1 is equal to 0.0 and prop 2 is equal to 0.2');
  //
  testclass.float1 := 0.1;
  testclass.float2 := 0.3;
  writeln( 'then we set the prop 1 to 0.1 and prop 2 to 0.3');
  writeln('');
  //
  writeln( 'we reload the state saved when the prop 1 was equal to 0.0 and prop 2 to 0.2   and we get:');
  str.Position := 0;
  str.ReadComponent(testclass);
  writeln( Format( 'prop 1 equal to %.2f', [testclass.float1]));
  writeln( Format( 'prop 2 equal to %.2f', [testclass.float2]));
  //
  writeln('');
  writeln('prop 1 has not been restored because the default value 0.0 was not written');
  writeln('prop 2 has been restored because a non default value was written and read');
  //
  ReadLn;
  str.free;
  testclass.free;
end.
4

1 回答 1

6

原来我被这个问题弄糊涂了。事实上,默认属性在这里不相关,因为实值属性不能有默认值。

事实上,如果实值属性的值为 0,该框架不会流出实值属性。这意味着实值属性实际上具有硬编码的默认值 0。这似乎是流式处理框架中的一个巨大的设计缺陷。

想象一下,1在组件的构造函数中分配了一个值的真实值属性。这使得该属性无法被分配一个值,0并且该值无法在 .dfm 文件的往返过程中继续存在。

有一种方法可以解决此问题,但您需要覆盖DefineProperties

type
  TMyComponent = class(TComponent)
  private
    FValue: Double;
    procedure WriteValue(Writer: TWriter);
  protected
    procedure DefineProperties(Filer: TFiler); override;
  public
    constructor Create(AOwner: TComponent); override;
  published
    property Value: Double read FValue write FValue;
  end;

constructor TMyComponent.Create(AOwner: TComponent);
begin
  inherited;
  FValue := 1.0;
end;

procedure TMyComponent.DefineProperties(Filer: TFiler);
begin
  inherited;
  Filer.DefineProperty('Value', nil, WriteValue, True);
end;

procedure TMyComponent.WriteValue(Writer: TWriter);
begin
  Writer.WriteDouble(FValue);
end;

VCL 错误位于Classes单元中,在IsDefaultPropertyValue. 该函数内部是这个本地函数:

function IsDefaultFloatProp: Boolean;
var
  Value: Extended;
begin
  Value := GetFloatProp(Instance, PropInfo);
  if AncestorValid then
    Result := Value = GetFloatProp(Ancestor, PropInfo)
  else
    Result := Value = 0;
end;

显然这个函数应该这样实现:

function IsDefaultFloatProp: Boolean;
var
  Value: Extended;
begin
  Value := GetFloatProp(Instance, PropInfo);
  if AncestorValid then
    Result := Value = GetFloatProp(Ancestor, PropInfo)
  else
    Result := False;
end;

同样的错误似乎也存在于Int64,Variantstring属性的处理中。这是一个重大缺陷,我希望它是众所周知的。我希望在 SO、Emba 论坛和一些 QC 报告上已经有很多关于这个主题的讨论。就是这样,一份来自时间黎明的 QC 报告:QC#928。由于该报告已有 10 多年的历史并且显得奄奄一息,我创建了QC#109194

于 2012-10-01T11:23:46.487 回答