![]() |
Delphi-Version: 2009
Bookmark and DisableControls - ein kleiner Trick mit Interfaces
Hallo zusammen,
ich bitte um Eure Meinung zu folgendem Vorgehen bei einem für mich alltäglichen Programmierfall: Sehr häufig stehe ich vor der Situation, dass ich mit einem Dataset (meist eine Komponente auf einem Datenmodul) etwas tun möchte, was aber den Cursor im Dataset verändern wird. Da das Dataset noch mit anderen visuellen Komponenten verbunden ist, oder warum auch immer, möchte ich den Cursor aber beibehalten. Also mache ich oft folgendes:
Delphi-Quellcode:
Das allgemeine Muster ist also:
procedure TuIrgendWas;
var lBookmark : TArray<Byte>; begin lBookmark := Dataset.Bookmark; Dataset.DisableControls; try { tu hier irgendwas, z.B. } Dataset.First; while not Dataset.Eof do begin { z.B. Feld in Datensatz aktualsisieren } Dataset.Edit; Dataset.FieldByName('markiert').AsInteger := 1; Dataset.Post; Dataset.Next; end; finally Dataset.Bookmark := lBookmark; Dataset.EnableControls; end; end;
Delphi-Quellcode:
Meistens ist das dann auch schon alles in der jeweiligen Prozedur. Ich suchte also eine Möglichkeit, den Rahmen um die eigentliche Arbeit schöner zu machen. Dabei bin ich auf folgende Lösung gekommen:
var
lBookmark : TArray<Byte>; begin lBookmark := Dataset.Bookmark; Dataset.DisableControls; try { tu hier irgendwas} finally Dataset.Bookmark := lBookmark; Dataset.EnableControls; end; Ich habe irgendwo ein Interface definiert, das ein Objekt repräsentiert, das sich beim Erzeugen sowohl das Bookmark als auch das Dataset merkt und DisableControls aufruft. Wenn man danach eh die Procedure verläßt, wird auch das Interface wieder freigegeben und dabei dann der Cursor/das Bookmark zurückgesetzt und EnableControls aufgerufen. Hier eine kleine Unit mit Interface und Classe:
Delphi-Quellcode:
Nun kann man den obigen Code so abkürzen:
unit uIntfTest;
interface uses Data.DB; type IDBTool = interface(IInterface) ['{D924218E-DF15-486F-9322-A989DEF76B50}'] end; TDBTool = class(TInterfacedObject, IDBTool) private FBookmark: TArray<byte>; FDataSet: TDataSet; procedure BeginUpdate; procedure EndUpdate; public constructor Create(const mDataSet: TDataSet); destructor Destroy; override; end; function BookmarkAndDisable(const mDataset: TDataSet): IDBTool; implementation function BookmarkAndDisable(const mDataset: TDataSet): IDBTool; begin Result := TDBTool.Create(mDataset); end; constructor TDBTool.Create(const mDataSet: TDataSet); begin inherited Create; FDataSet := mDataSet; BeginUpdate; end; destructor TDBTool.Destroy; begin EndUpdate; inherited; end; procedure TDBTool.BeginUpdate; begin FBookmark := FDataSet.Bookmark; FDataSet.EnableControls; end; procedure TDBTool.EndUpdate; begin FDataSet.Bookmark := FBookmark; FDataSet.EnableControls; end; end.
Delphi-Quellcode:
Delphi kümmert sich beim Freigeben des Interfaces, also am Ende der Prozedur automatisch um das Zurücksetzen des Bookmarks und die Control-Aktualisierung. Wenn man es früher braucht, müsste man doch mit einer lokalen Variablen arbeiten:
procedure TuIrgendWas;
begin BookmarkAndDisable(Dataset); { tu hier irgendwas } end;
Delphi-Quellcode:
procedure TuIrgendWas;
var lIntf: IDBTool; begin lIntf := BookmarkAndDisable(Dataset); { tu hier irgendwas } lIntf := nil; { tu nochmal was } end; Was meint ihr? Ist das eine brauchbare Lösung? Gibt es irgendwelche Pferdefüße? |
AW: Bookmark and DisableControls - ein kleiner Trick mit Interfaces
Das Interface so einzusetzen sollte m.E. in Ordnung gehen.
Du musst halt darauf achten, keine weitere Referenz auf dieses Interface zu definieren, da dieses sonst nicht freigegeben wird. Generell könnte man noch überlegen, solche nebenläufigen Datenänderungen direkt über ein SQL-Statement durchzuführen. Dann hätte das keine Auswirkungen auf den DB-Cursor. |
AW: Bookmark and DisableControls - ein kleiner Trick mit Interfaces
In
Delphi-Quellcode:
sollte besser
BeginUpdate
Delphi-Quellcode:
verwendet werden.
DisableControls
|
AW: Bookmark and DisableControls - ein kleiner Trick mit Interfaces
@Uwe Rabe: Vielen Dank - das ist natürlich ein Fehler
Delphi-Quellcode:
procedure TDBTool.BeginUpdate;
begin FBookmark := FDataSet.Bookmark; FDataSet.DisableControls; end; |
AW: Bookmark and DisableControls - ein kleiner Trick mit Interfaces
Wenn du wirklich eine Meinung möchtest:
Es ist zwar spaßig, mittels ARC Dinge auszulösen (wie z.B. hier beim TDataSet). Aber abseits von ![]() Angenommen ich bin neu bei euch und sehe deinen Code zum ersten mal. Beim
Delphi-Quellcode:
-Muster ist auf den ersten Blick alles klar.
Dataset.DisableControls();
try { tu hier irgendwas} finally Dataset.EnableControls; end; Im Fall von
Delphi-Quellcode:
sparen wir zwar zwei Zeilen, aber es ist auf den ersten Blick überhaupt nicht ersichtlich was im Hintergrund wirklich passiert. Wenn man den Namen gut wählt sollte das zwar kein Problem sein, aber in anderen Fällen wird das vielleicht doch mal wichtig.
procedure TuIrgendWas;
begin BookmarkAndDisable(Dataset); { tu hier irgendwas } end; Beim Refactoring sehe ich bei so etwas auch eine Fehlerquelle (diskutiert vor etwas längerer Zeit): ![]() Es gibt Fälle wo so etwas eine tolle Sache (mir fallen spontan nur Smart Pointer ein), aber dieser hier gehört mMn nicht dazu - Dafür ist der bestehende Code mit dem try..finally viel zu eindeutig und gut. |
AW: Bookmark and DisableControls - ein kleiner Trick mit Interfaces
Sehe ich ähnlich wie Günther. Du hättest wahrscheinlich deine Freude mit C++, da dort das RAII Prinzip zum Einsatz kommt. Solche "Guards" sind da ganz normal. Ich persönlich finde es aber auch ziemlich unübersichtlich, wenn der "finally" Teil vor dem "eigentlichen Code" steht.
Wenn du dir die immer gleichen try..finally Blöcke sparen willst, könnte auch eine anonyme Prozedur ganz interessant sein:
Delphi-Quellcode:
Aufruf dann so:
type
TMyAnonProc = reference to procedure(const Dataset: TDataset); TDatasetHelper = record public class procedure MyProcedure(const Dataset: TDataset; Proc: TMyAnonProc); static; end; { TDatasetHelper } class procedure TDatasetHelper.MyProcedure(const Dataset: TDataset; Proc: TMyAnonProc); var lBookmark : TArray<Byte>; begin lBookmark := Dataset.Bookmark; Dataset.DisableControls; try Proc(Dataset); finally Dataset.Bookmark := lBookmark; Dataset.EnableControls; end; end;
Delphi-Quellcode:
TDatasetHelper.MyProcedure(Dataset, procedure(const Dataset: TDataset)
begin // do something end); |
AW: Bookmark and DisableControls - ein kleiner Trick mit Interfaces
Pssst, per Class-Helper als Funktion and TDataSet hängen, anstatt als irgendwo rumliegende eigenständige Funktion.
So hat man auch mehr Spaß am CodeInsigt/CodeVervollständigung. Und nein, man kann sich auch das Interface speichern, um schon vor Prozedurende das Freizugeben. Also das läuft ja bis zum END.
Delphi-Quellcode:
Aber hier ist vorher Schluss.
begin
DataSet.BookmarkAndDisable; ...machwas end;
Delphi-Quellcode:
Da wir hier ein Interface haben, können wir das Try-Finally auch weglassen, denn Delphi baut implizit ein Try-Finally für solche lokalen Variablen ein, um sie freizugeben/finalisieren.
var
Bookmark: IDBTool; // oder IInterface begin Bookmark := DataSet.BookmarkAndDisable; try ...machwas finally Bookmark := nil; // alternativ kann man auch ein Bookmark.EnableBookmark; anbieten end; ...machnochwas end;
Delphi-Quellcode:
var
Bookmark: IDBTool; // oder IInterface begin Bookmark := DataSet.BookmarkAndDisable; ...machwas Bookmark := nil; ...machnochwas end; |
AW: Bookmark and DisableControls - ein kleiner Trick mit Interfaces
Zitat:
Mir ist die (potentielle) Verwirrung die zwei gesparten Zeilen echt nicht wert. |
AW: Bookmark and DisableControls - ein kleiner Trick mit Interfaces
Zitat:
![]() |
AW: Bookmark and DisableControls - ein kleiner Trick mit Interfaces
Wieder was neues kennengelernt, danke! :thumb:
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 19:04 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz