デフォルトの組み込み「コンポーネント ストリーミング システム」を使用する特定の方法で、プロパティの値がデフォルト値と等しい場合にその値が書き込まれないことが問題であることがわかりました。
次のスキームを考えてみましょう。
コンポーネントの特定の状態を保存するには、 WriteComponent ()とReadComponent( ) を使用します。この状態をプリセットと呼びます。コンポーネントには、setter を持つさまざまな Real 型のプロパティが含まれています。
プロパティがデフォルト値と等しい場合、プリセットにはその値が含まれないことがわかっています。
だから私たちのコンポーネントのために
- プロパティAFLoatを0.0に設定します。
- プリセットをストリームに保存します (MyStream.WriteComponent(MyInstance))
- プロパティ AFLoatを0.101に設定します
- プリセットをリロードします (MyStream.ReadComponent(MyInstance))
最後に、プリセットのリロード後、AFLoatの値は0.0であると予想していましたが、まだ0.101のままです。
バグの原因は、プロパティのデフォルト値がコンポーネント ストリームに書き込まれないことです。したがって、ステップ2では、プロパティは書き込まれず、ステップ 4 では読み取られません...かなり面倒ですよね!
プロパティのデフォルト値を強制的にコンポーネント ストリームに書き込む方法はありますか? 実際、実数型のプロパティに対する自家製の修正がありますが、根本的な問題を克服するためのよく知られた方法があるかどうかを知りたいです。
私のカスタム修正は、 ReadComponent()を呼び出す前に TypInfos を使用して、Real 型のプロパティを 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.