![]() |
Record-Variable in Klasseninstanz setzen
Moin allerseits,
ich bin offensichtlich noch nicht so der Held in der Erstellung und Verwendung von Klassen, deshalb hab ich damit auch noch ein paar Verständnis-Probleme ... ich arbeite übrigens bis Ende des Jahres an einem Win7-Kundenrechner mit RadStudio XE2 Architect und nicht wie gewohnt im RadStudio 2009 pro ... In einer Klasse, die hauptsächlich auf die Mainform zugreift und dort Dinge wie Statusbar und diverse Label-Captions verändern soll, habe ich einen Record definiert:
Delphi-Quellcode:
Wenn ich nun in einer AfterScroll-Ereignisbehandlung, die sich in der Unit zur Form DatMod (Datamodule) befindet, die Variable User_Tab.Titel_Index setzen möchte, erhalte ich eine Zugriffsverletzung:
unit Main_Zugriff;
INTERFACE USES Vcl.Forms, System.Classes, System.SysUtils, JvRichEdit, JvDBRichEdit; TYPE TMain_Zugriff = CLASS PRIVATE PUBLIC TYPE TTab_User = RECORD Titel_Index : Integer; Titel_SortIndex : Integer; Titel_SortOrder : Boolean; Titel_SortText : String; END; VAR User_Tab : TTab_User; CONSTRUCTOR Create(); DESTRUCTOR Destroy; override; PROCEDURE Set_FormMain(Const Links, Oben, Breit, Hoch : Integer; Const Maximiert : Boolean); PROCEDURE Get_FormMain(Var Links, Oben, Breit, Hoch : Integer; Var Maximiert : Boolean); PROCEDURE Set_StatusBar(Const Spalte : Integer; Const Wert : String); END; IMPLEMENTATION USES UnitMain; Im Projekt MovieBase3.exe ist eine Exception der Klasse $C0000005 mit der Meldung 'c0000005 ACCESS_VIOLATION' aufgetreten.
Delphi-Quellcode:
Ein weitgehend ähnliches Konstrukt im selben Projekt funktioniert aber einwandfrei:
...
VAR DatMod: TDatMod; IMPLEMENTATION {%CLASSGROUP 'System.Classes.TPersistent'} {$R *.dfm} USES Main_Zugriff; VAR MainZu : TMain_Zugriff; procedure TDatMod.DataModuleCreate(Sender: TObject); begin MainZu := TMain_Zugriff.Create; end; procedure TDatMod.DataModuleDestroy(Sender: TObject); begin FreeAndNil(MainZu); end; procedure TDatMod.Aset_TitelAfterScroll(DataSet: TDataSet); VAR Idx_Titel : Integer; begin Idx_Titel := Aset_Titel.FieldByName('Idx_Titel').AsInteger; MainZu.Set_StatusBar(0,'Idx: ' + IntToStr(Idx_Titel)); MainZu.Set_StatusBar(1,'Anzahl: ' + IntToStr(Aset_Titel.RecordCount)); MainZu.Set_StatusBar(2,'RecNo: ' + IntToStr(Aset_Titel.RecNo)); MainZu.User_Tab.Titel_Index := Idx_Titel; // hier wird die Zugriffsverletzung ausgelöst end;
Delphi-Quellcode:
Wenn ich hier vom Hauptformular aus z.B. die Variable Rec_User.Pfad_Main setze, gibt's keine Zugriffsverletzung:
UNIT MainTools;
INTERFACE USES System.SysUtils, System.Classes, Vcl.Dialogs, Data.DB, Data.Win.ADODB, Datasnap.DBClient, Data.DBXFirebird, Data.SqlExpr, Data.FMTBcd, Datasnap.Provider; TYPE TMainTools = CLASS PRIVATE CONST K = ';'; Km = ','; cPROV_Name = 'SQLOLEDB.1'; cDB_Name = 'FILME'; cServer_Name = 'SPEEDY'; cInt_Secur = 'SSPI'; cUser_Name = 'MsMasterAdmin'; cApp_Name = 'MovieBase3'; VAR PROV_Name, DB_Name, Server_Name, Int_Secur, User_Name, App_Name, Verbindung : String; PUBLIC TYPE TRec_User = RECORD Pfad_Main, Name, Pass : String; END; VAR Rec_User : TRec_User; SuchText : String; CONSTRUCTOR Create(); DESTRUCTOR Destroy; override; FUNCTION Verbinden_Datenbank : Boolean; PROCEDURE Trennen_Datenbank; FUNCTION Verbinden_Datasets : Boolean; PROCEDURE Trennen_Datasets; PROCEDURE Einstellungen_Setzen; PROCEDURE Einstellungen_Speichern; END; IMPLEMENTATION USES UnitData, Main_Zugriff; VAR MZGF : TMain_Zugriff;
Delphi-Quellcode:
...
var FormMain: TFormMain; IMPLEMENTATION {$R *.dfm} USES MainTools, UnitData; VAR MNTL : TMainTools; procedure TFormMain.FormCreate(Sender: TObject); begin MNTL := TMainTools.Create; MNTL.Rec_User.Pfad_Main := ExtractFilePath(ParamStr(0)); // keine Access Violation MNTL.SuchText := ''; // keine Access Violation end; ... |
AW: Record-Variable in Klasseninstanz setzen
Funktioniert es denn, wenn Du von Nested Types auf "normale" umsteigst? Wobei mir übrigens der Sinn so einer Klasse nicht ganz klar ist.
|
AW: Record-Variable in Klasseninstanz setzen
Zitat:
Delphi-Quellcode:
Das ändert jedoch nichts, es erscheint derselbe Fehler. Nun habe ich der Klasse TMain_Zugriff eine weitere Procedure beigefügt, die User_Tab.Titel_Index ändern soll:
unit Main_Zugriff;
INTERFACE USES Vcl.Forms, System.Classes, System.SysUtils, JvRichEdit, JvDBRichEdit; TYPE TTab_User = RECORD Titel_Index : Integer; Titel_SortIndex : Integer; Titel_SortOrder : Boolean; Titel_SortText : String; END; TMain_Zugriff = CLASS PRIVATE PUBLIC User_Tab : TTab_User; Titel_Index : Integer;
Delphi-Quellcode:
Rufe ich diese Procedure von Create aus auf, dann funktioniert sie:
procedure TMain_Zugriff.Set_Titel_Index(Idx: Integer);
begin User_Tab.Titel_Index := Idx; Set_StatusBar(4,IntToStr(User_Tab.Titel_Index)); end;
Delphi-Quellcode:
Rufe ich sie jedoch von Aset_TitelAfterScroll (DatMod) aus auf, verweigert sie den Dienst:
constructor TMain_Zugriff.Create;
begin Set_Titel_Index(11111); end;
Delphi-Quellcode:
Die anderen Proceduren wie z.B. Set_StatusBar funktionieren aber von Aset_TitelAfterScroll aus ... aber die ändern auch keine Variablen der eigenen Klasse, sondern die einer anderen Klasse (TFormMain).
procedure TDatMod.Aset_TitelAfterScroll(DataSet: TDataSet);
VAR Idx_Titel : Integer; begin ... MainZu.Set_Titel_Index(Idx_Titel); end; Der Sinn dieser Klasse besteht darin (oder soll darin bestehen), nicht vom Datenmodul aus die Mainform referenzieren zu müssen, wenn Ereignisse im Datenmodul Veränderungen in der Mainform erfordern. Doch warum eigentlich nicht, die Mainform referenziert ja das Datenmodul nicht, das läuft alles über die Unit MainTools. Deshalb hab ich das jetzt so gemacht: In der Unit UnitData (Form DatMod) hab ich als ersten Type meine Record-Deklaration platziert. Im Public-Teil von DatMod steht jetzt die Variablen-Deklaration des Records. So gibt's keine Zugriffsverletzung mehr ... Ob das die endgültige Lösung ist, wird sich zeigen. Aber ich glaube, daß ich sowieso irgendwie auf dem Holzweg bin mit meiner Vorstellung über die Trennung von GUI und Methoden ... Vielleicht sollte ich mal SirRufo fragen, wie eine Kapselung für Datenbank-Komponenten aussehen muß ... immerhin propagiert er die ja des öfteren mal ... |
AW: Record-Variable in Klasseninstanz setzen
Ich vermute mal, daß das AfterScroll-Event vor dem DataModuleCreate ausgelöst wird (passiert, wenn das DataSet zur DesignTime auf Active gesetzt ist). Damit wäre die Instanz von MainZu noch nicht erstellt und es kommt zu der Schutzverletzung.
|
AW: Record-Variable in Klasseninstanz setzen
Zitat:
So wie ich's nun gelöst habe, ist es sowieso viel übersichtlicher: Die Variablen, die andere Units bzw. Klassen über den Zustand der Datenbank informieren sollen, lege ich im Datenmodul ab. Von dort holt sich die Klasse TMainTools alle notwendigen Informationen. |
AW: Record-Variable in Klasseninstanz setzen
Wird denn die Variable von TMain_Zugriff
VAR User_Tab : TTab_User; initialisiert? |
AW: Record-Variable in Klasseninstanz setzen
Zitat:
Delphi-Quellcode:
Was ich nicht verstanden hatte: Wieso funktionierte ein ähnliches Konstrukt in einer anderen Klasse (und Unit) vom Hauptformular aus und das im ersten Post gezeigte Konstrukt vom Datenmodul aus nicht?
procedure TMainTools.Einstellungen_Setzen;
CONST N = '0'; VAR i,z, Links, Oben, Breit, Hoch : Integer; Maximiert : Boolean; Aus : String; begin IF NOT DatMod.Aset_Benutzer.Active THEN EXIT; Rec_User.User_Index := DatMod.Aset_Benutzer.FieldByName('Idx_Benutzer').AsInteger; Rec_User.Name := DatMod.Aset_Benutzer.FieldByName('Name').AsString; Rec_User.Pass := DatMod.Aset_Benutzer.FieldByName('Passwort').AsString; Links := DatMod.Aset_Benutzer.FieldByName('Links').AsInteger; Oben := DatMod.Aset_Benutzer.FieldByName('Oben').AsInteger; Breit := DatMod.Aset_Benutzer.FieldByName('Breit').AsInteger; Hoch := DatMod.Aset_Benutzer.FieldByName('Hoch').AsInteger; Maximiert := DatMod.Aset_Benutzer.FieldByName('Maximiert').AsBoolean; MZGF.Set_FormMain(Links, Oben, Breit, Hoch, Maximiert); IF NOT DatMod.Aset_UserTabs.Active THEN EXIT; IF NOT DatMod.Aset_UserTabs.Locate('Idx_User_Tabs',Rec_User.User_Index,[]) THEN EXIT; // Filmtitel - Initialisierung von User_Tab DatMod.User_Tab.Titel_Index := DatMod.Aset_UserTabs.FieldByName('Titel_Index').AsInteger; DatMod.User_Tab.Titel_SortIndex := DatMod.Aset_UserTabs.FieldByName('Titel_SortIndex').AsInteger; DatMod.User_Tab.Titel_SortOrder := NOT DatMod.Aset_UserTabs.FieldByName('Titel_SortOrder').AsBoolean; DatMod.User_Tab.Titel_SortText := DatMod.Aset_UserTabs.FieldByName('Titel_SortText').AsString; DatMod.Aset_Titel.Locate('Idx_Titel',DatMod.User_Tab.Titel_Index,[]); Sortieren_FilmTitel(DatMod.User_Tab.Titel_SortIndex); z := MZGF.Spalten_Filmtitel -1; FOR i := 0 TO z DO BEGIN Aus := IntToStr(i); IF i < 10 THEN Aus := N + Aus; Aus := 'Tab_Titel_' + Aus; MZGF.Set_Spalte_FilmTitel(i,DatMod.Aset_UserTabs.FieldByName(Aus).AsInteger); END; end; Das AfterScroll-Ereignis im Datenmodul wird bereits vor dem Setzen der Werte in MainTool ausgelöst, nach dem ersten Dataset.Open (korrekt: DatMod.User_Tab.Active := true) wird es garantiert ausgelöst – so dürfte der nicht-initialisierte Zustand der Variablen aber dennoch keine Rolle spielen, da der Wert der Variablen ja nicht ausgelesen, sondern gesetzt wird ... Das war es doch, worauf deine Frage zielte, oder? Ich glaub ich muß für heute mal aufhören, sitze schon seit 5:30 hier ... |
AW: Record-Variable in Klasseninstanz setzen
Zitat:
|
AW: Record-Variable in Klasseninstanz setzen
Zitat:
|
AW: Record-Variable in Klasseninstanz setzen
Zeig mal den constructor von TMain_Zugriff.
Probier eventl. mal das. Wenn das funzt liegt Uwe wohl richtig:
Delphi-Quellcode:
procedure TDatMod.DataModuleCreate(Sender: TObject);
begin Aset_TitelAfterScrollFlag:= false; MainZu := TMain_Zugriff.Create; Aset_TitelAfterScrollFlag:= true; end; procedure TDatMod.Aset_TitelAfterScroll(DataSet: TDataSet); begin if Aset_TitelAfterScrollFlag then begin .. end; end; |
AW: Record-Variable in Klasseninstanz setzen
Zitat:
Übrigens: ein recht bekanntes Beispiel ist die Methode Free, die man auch aufrufen kann, wenn die entsprechende Instanzvariable nil ist. |
AW: Record-Variable in Klasseninstanz setzen
Zum Testen:
Delphi-Quellcode:
type
TFoo = class private FData : string; function GetData : string; public property Data : string read GetData; end; function TFoo.GetData : string; begin if Self = nil then Result := 'keine Instanz vorhanden' else Result := FData; end; procedure Test; var LFoo : TFoo; begin ShowMessage( LFoo.Data ); end; |
AW: Record-Variable in Klasseninstanz setzen
Mann ist das ein Kuddelmuddel in dem Sourcecode!
Ich versuche mal Ordnung zu schaffen. Wir beginnen mit dem Record:
Delphi-Quellcode:
und stellen die wichtigste Frage:
TTab_User = RECORD
Titel_Index : Integer; Titel_SortIndex : Integer; Titel_SortOrder : Boolean; Titel_SortText : String; END; Könnte es nicht sein, dass TTab_User nicht eine Klasse statt einen Records sein sollte? Eine Klasse ist im Prinzip ein Record + Methoden. Antwort: Ja, es gibt potentielle Methoden für die Klasse TTab_User:
Delphi-Quellcode:
Und damit sieht die Klasse im 1. Entwurf so aus:
// Filmtitel - Initialisierung von User_Tab
DatMod.User_Tab.Titel_Index := DatMod.Aset_UserTabs.FieldByName('Titel_Index').AsInteger; DatMod.User_Tab.Titel_SortIndex := DatMod.Aset_UserTabs.FieldByName('Titel_SortIndex').AsInteger; DatMod.User_Tab.Titel_SortOrder := NOT DatMod.Aset_UserTabs.FieldByName('Titel_SortOrder').AsBoolean; DatMod.User_Tab.Titel_SortText := DatMod.Aset_UserTabs.FieldByName('Titel_SortText').AsString;
Delphi-Quellcode:
Der Anfang ist gemacht.
TTab_User = class(TObject)
public TitelIndex : Integer; SortIndex : Integer; SortOrder : Boolean; SortText : String; procedure LoadFromDataset(ds:TDataset); procedure SaveToDataset(ds:TDataset); procedure Clear; END; procedure TTab_User.LoadFromDataset(ds:TDataset); begin TitelIndex:= ds.FieldByName('Titel_Index').AsInteger; SortIndex := ds.FieldByName('Titel_SortIndex').AsInteger; SortOrder := NOT ds.FieldByName('Titel_SortOrder').AsBoolean; SortText := ds.FieldByName('Titel_SortText').AsString; end; Die weiteren Schritte sehen so aus: * aus den public Variablen werden Properties gemacht * weitere Methoden (z.B. procedure Clear oder SaveToDataset)entdecken und zur Klasse hinzufügen * überprüfen ob die Klasse vollständig ist (gibt es noch weitere Felder aus der Tabelle "User_Tab"?) * den Sourcecode der Klasse in eine eigene Unit verschieben * Ein Objekt der neuen Klasse TTab_User in die Klasse TMain_Zugriff einbauen * überlegen ob ein Objekt von TTab_User ausreicht oder ob man nicht eine Liste von diesen Objekten braucht * müssen TTab_User-Objekte kopiert werden können? Fall ja, von TPersistent ableiten und die Methode Assign überschreiben |
AW: Record-Variable in Klasseninstanz setzen
Zitat:
Hier mal ein bisschen mehr zum Spielen:
Delphi-Quellcode:
Der Class-Constructor könnte zur Zeit noch entfallen, da der Compiler auch Klassen-Variablen initialisiert, aber das muss ja nicht immer so bleiben.
type
TFoo = class private class var FCount: Integer; private FData : string; class constructor Create; function GetData : string; public constructor Create; property Data : string read GetData; end; constructor TFoo.Create; begin inherited; Inc(FCount); FData := 'Instanz: ' + IntToStr(FCount); end; class constructor TFoo.Create; begin FCount := 0; end; function TFoo.GetData : string; begin if Self = nil then Result := 'keine Instanz vorhanden' else Result := FData; end; procedure Test(init, nilit: Boolean); var LFoo : TFoo; begin if init then LFoo := TFoo.Create else if nilit then LFoo := nil; ShowMessage( LFoo.Data ); end; begin Test(false, true); Test(true, false); Test(false, false); end. |
AW: Record-Variable in Klasseninstanz setzen
Zitat:
Das kann doch kein Mensch lesen. (Lesen ist was anderes als "mit Mühe Entziffern" :!:) |
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:50 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