3

もちろん、この例は単純化されていますが、基本的には、別のフォーム (frmSettings) をトリガーするメイン フォームがあります。

function Execute(var aSettings: TSettings):Boolean

TSettings設定を追跡するためにメインフォームで作成された私自身のオブジェクトです。

この新しく開いたフォーム (frmSettings)TMyObjectListで、 の子孫である をフェッチしTObjectListます。でいっぱいTMyObjです。

次にTListBox、その TMyObjectList から値を入力します。

コード:

...

FMyObjectList : TMyObjectList;
property MyObjectList: TMyObjectList read getMyObjectList;

...

function TfrmSettings.getMyObjectList: TMyObjectList ;
begin
    If not Assigned(FMyObjectList) then FMyObjectList := TMyObjectList.Create(True)
    Result := FMyObjectList;
end;

function TfrmSettings.Execute(var aSettings: TSettings): Boolean;
begin

    //Fill myObjectList
    FetchObjs(myObjectList);

    //Show list to user
    FillList(ListBox1, myObjectList);

    //Show form        
    ShowModal;

    Result := self.ModalResult = mrOk;

    if Result then
    begin
        // Save the selected object, but how??

        // Store only pointer? Lost if list is destroyed.. no good
        //Settings.selectedObj := myObjectList.Items[ListBox1.ItemIndex];

        // Or store a new object? Have to check if exist already?
        If not Assigned(Settings.selectedObj) then Settings.selectedObj := TMyObj.Create;
        Settings.selectedObj.Assign(myObjectList.Items[ListBox1.ItemIndex];);
    end;

end;

procedure TfrmSettings.FillList(listBox: TListBox; myObjectList: TMyObjectList);
var
    i: Integer;
begin
    listBox.Clear;
    With myObjectList do
    begin
        for i := 0 to Count - 1 do
        begin
            //list names to user
            listBox.Items.Add(Items[i].Name);
        end;
    end;
end;

procedure TfrmSettings.FormDestroy(Sender: TObject);
begin
    FreeAndNil(FMyObjectList);
end;

ポインターだけを保存するのは良い考えではないようです。設定フォームを再度トリガーすると、リストが再作成され、ユーザーが「キャンセル」を押しても元のオブジェクトが失われるためです。

したがって、すべてのプロパティを正しく取得するには、割り当てを使用して、コピーを保存する方が良いようです。そして、最初にオブジェクトが既にあるかどうかを確認します。

        If not Assigned(Settings.selectedObj) then Settings.selectedObj := TMyObj.Create;
        Settings.selectedObj.Assign(myObjectList.Items[ListBox1.ItemIndex];);

これらの2行を代わりにメソッドに移動する必要がありますSettings.AssignSelectedObj(aMyObj:TMyObj)

これは正しいように見えますか、それとも間違った方法で実装していますか? 多かれ少なかれ必要なものはありますか?

メモリリークやその他のトラブルに巻き込まれないように、より安全に感じるように、いくつかのガイドラインが必要です。

コードを少し確認する以外に、本当の問題は次のとおりです。これは、SelectedObject を設定クラスに格納する正しい方法ですか?

4

3 に答える 3

1

これは、選択したオブジェクトを設定に保存する正しい方法ですか?

おそらくそうではありません。設定クラスは、フォームに依存するべきではありません。ユーザーが設定を開くたびにフォームを動的に作成および破棄することにした場合はどうなるでしょうか。この場合、設定は無効なオブジェクト参照を保持します。

私見では、オブジェクトリストを選択したオブジェクトのインデックスとともに設定に保存することをお勧めします。フォームは設定にアクセスし、リスト ボックスに入力し、ユーザーが [OK] で確認した後、選択したオブジェクト インデックスを変更する必要があります。

コードでメモリ リークが発生しています。TObjectListをローカル変数として作成しますが、解放することはありません。また、ローカル変数を解放すると、リストボックス内のオブジェクト参照が無効になります。次の 2 つのオプションがあります。

  • オブジェクト リストをフォームのメンバー変数として保存し、イベント ハンドラーで作成し、FromCreateイベント ハンドラーで破棄しFormDestroyます。その後、リスト ボックスでオブジェクト参照を安全に使用できます。

  • オブジェクト リストを外部のどこかに保存し、Executeメソッドのパラメーターとしてフォームに渡します。このシナリオでは、オブジェクト参照も安全に使用できます。

于 2011-01-31T14:15:44.333 に答える
0

myObjectList の名前を GlobalObjectList に変更し、クラスの外に移動します。フォームで宣言できますが、初期化/終了セクションで作成/解放します。初期化中、リストを作成した後、ini ファイル (または保存した場所) から入力します。これで、自分のユニットが Uses にある場所ならどこからでもアクセスできます。

于 2011-01-31T14:17:31.470 に答える
0

TSettings のシリアル化はどうですか? いくつかの公開されたプロパティに設定を入れて、RTTI にその内容を保存させます。

type
  TSettings = class(TPersistent)
  public
    function SaveAsText: UTF8String;
  end;

function TSettings.SaveAsText: UTF8String;
begin
var
  Stream1, Stream2: TMemoryStream;
begin
  Stream1 := TMemoryStream.Create;
  Stream2 := TMemoryStream.Create;
  try
    Stream1.WriteComponent(MyComponent);
    ObjectBinaryToText(Stream1, Stream2);
    SetString(result,PAnsiChar(Stream2.Memory),Stream2.Size);
  finally
    Stream1.Free;
    Stream2.Free;
  end;
end;

次に、設定をテキスト ファイルまたはテキスト文字列に保存できます。

それはただ一つの解決策です。しかし、設定をテキストとして保存することは非常に便利です。フレームワークでこのようなアプローチを使用して、コード生成されたユーザー インターフェイスを介して設定を保存します。TPersistent インスタンスのツリーから、設定ツリーが作成されます。

于 2011-01-31T16:16:35.877 に答える