![]() |
Getter wird übergangen
Ahoi!
Ich habe hier folgende Struktur (eine Objekt-Relationale Abbildung zu einer DB, Delphi 2010):
Delphi-Quellcode:
Um die ID eines Users anhand seines Logins zu erhalten, tue ich in einer anderen Unit dies:
TDB = class
private type TDBUser = class private class var FID: Integer; class function GetName: string; static; class procedure SetName(const Value: string); static; // für jedes DB feld eben... public class property ID: Integer read FID; class property Name: string read GetName write SetName; end; class var FQuery: TUniQuery; class var FUser: TDBUser; class function GetDBUserByLogin(aLogin: string): TDBUser; static; public class property UsersByLogin[aLogin: string]: TDBUser read GetDBUserByLogin; end; implementation class function TDB.GetDBUserByLogin(aLogin: string): TDBUser; begin Query.SQL.Text := 'SELECT id FROM users WHERE login=:lg'; Query.ParamByName('lg').AsString := aLogin; Query.Open; if Query.RecordCount=0 then begin Query.Close; raise EUserDoesNotExistException.Create(); end; FUser.FID := Query.FieldByName('id').AsInteger; result := FUser; Query.Close; end;
Delphi-Quellcode:
An der markierten Stelle passiert es dann: NICHTS!
constructor TUser.Create(aLogin, aPassword: string);
var id: Integer; begin if FInstanceCounter > 0 then raise Exception.Create('Es ist bereits ein User angemeldet. Melden Sie diesen ab, bevor Sie sich einloggen.'); try id := TDB.UsersByLogin[aLogin].ID; // !!! Hier wichtig !!! :) except raise Exception.Create('Benutzer '''+aLogin+''' existiert nicht.'); end; if TDB.Users[id].Password <> aPassword then raise Exception.Create('Ungültiges Passwort für Benutzer '''+aLogin+'''.'); FId := id; FLogin := aLogin; FName := TDB.Users[id].Name; FSurname := TDB.Users[id].Surname; FRights := TDB.Users[id].Rights; inc(FInstanceCounter); end; Es wird niemals die Methode GetDBUserByLogin() durchlaufen, und "id" hat nachher immer den Wert "0" (diese ID gibt es nicht in der DB). Ich erhalte keine Hinweise/Warnungen vom Compiler, kann einen Haltepunkt auf die oben markierte Zeile setzen, ein solcher wird in der Methode GetDBUserByLogin() jedoch deaktiviert. (Das ist die einzige Überladung davon.) Löschen der DCUs hat leider nichts gebracht, sowie auch das testweise entfernen des try-Blocks. Auch die Optimierung abzuschalten hat an diesem Verhalten nichts geändert. Bin es nun ich, der einen Denkfehler hat und ihn nicht findet, oder wäre das es wert dem Support vorzutragen? :gruebel: Besten Dank schon mal! Medium |
AW: Getter wird übergangen
Wieso hast du denn ausschliesslich statische class-Methoden?
Das ist sehr unschön und damit verbaust du Dir alle Möglichkeiten für die Zukunft. Die Klasse TDBUser sollte auf jeden Fall eine ganz normale Klasse sein; ungefähr so:
Delphi-Quellcode:
Die Funktion GetDBUserByLogin() sieht dann ungefähr so aus:
TDBUser = class
private FID: Integer; FName : string; public property ID: Integer read FID; property Name: string read GetName write SetName; property LastLogon:TDateTime read FLastLogon; end;
Delphi-Quellcode:
Der Aufrufer GetDBUserByLogin() muss dann das TDBUser-Objekt freigeben.
class function TDB.GetDBUserByLogin(aLogin: string): TDBUser;
begin Query.SQL.Text := 'SELECT id FROM users WHERE login=:lg'; Query.ParamByName('lg').AsString := aLogin; Query.Open; if Query.IsEmpty {besser als RecordCount=0} then begin Query.Close; raise EUserDoesNotExistException.Create(aLogin {welcher user nicht existiert ist wichtig zu wissen}); end; result := TDBUser.Create; // neues Obj erzeugen ! result.ID := Query.FieldByName('id').AsInteger; result.Name := Query.FieldByName('Name').AsString; .... Query.Close; end; Falls Dir das nicht gefällt, kann man auch auf Interfaces ausweichen. Damit werden die TDBUser-Objekte automatisch freigegeben (über die Referenzzählung). |
AW: Getter wird übergangen
Eben genau die Freigabefrage war der Grund für die Klassenmethoden. Es würde ja praktisch für jeden Zugriff auf einen User, wie klein auch immer, eine neue Instanz erstellt, die dann irgendwer freigeben müsste. Da die Instanz selber aber niemanden interessiert, sondern nur das, was die Properties zurück geben (das sind intern SQL Abfragen), wird auch nirgends eine Referenz auf diese behalten. Das möchte ich auch keinesfalls!
Da mir Interfaces recht unsympathisch sind, muss ich mal fragen, was ich mir so verbaue? Ableiten soll da ohnehin nie mehr jemand von, deswegen ist es ja eine private Klasse, und ich sollte sie ggf. auch noch sealed machen. Das ist auf eine ganz spezielle DB zugeschnitten, und wer auch immer darin etwas ändert hat a) auch Zugang zum Source, und bekommt b) was auffe Löffel :). Das seltsame Verhalten erklärt das allerdings leider noch nicht :? Edit: IsEmpty ist schöner, danke! Welcher User da nicht gefunden wurde, wird im Moment eine Aufruftiefe höher behandelt. Da weiss ich ja auch mit welchem Login ich nachgefragt habe, und on EUserDoesNotExistException wird jetzt explizit mit einem MessageDlg behandelt. Man könnte das noch als Parameter an die Exception geben, Sinn würd das auch machen. Denken :) Macht viel Sinn, habs angepasst. |
AW: Getter wird übergangen
OK, statt dem Interface würde ein Record das selbe Ergebnis bieten.
- mehrere solcher Container gleichzeitig möglich - Delphi kümmert sich automatisch um die Freigabe
Delphi-Quellcode:
[edit]
TDBUser = record
private FID: Integer; FName: string; FLastLogon: TDateTime; public property ID: Integer read FID; property Name: string read FName; property LastLogon: TDateTime read FLastLogon; end; in Delphi 7 dann ein normaler Record ohne Property (die waren eh nur für ein ReadOnly gedacht) |
AW: Getter wird übergangen
Das könnte sicherlich auch gut klappen, und wäre ggf. eine Überlegung wert. Ich hatte beim Basteln .NET im Hinterkopf, wo sowas dank GC ja usus ist.
Allerdings hilft mir das bei meinem eigentlichen Problem kein Stück weiter, da der Getter von TDB, nicht von TDB.TDBUser betroffen ist, also eine Ebene höher. Welchen Typ TDBUser dann hat, ist wohl relativ unerheblich hier, auch wenn ich die Ideen gerne annehme! Das hat nur leider nix mit dem Problem an sich zu tun :? |
AW: Getter wird übergangen
Erstmal wäre es gut, daß du erwähnst, wenn du eine andere Delphiversion, als die im Profil erwähnte, verwendest.
(inline-Typen gingen in D7 doch noch garnicht) ups, übersehn :oops: Dann kann es sein, daß der Compiler, Aufgrund deiner komischen Sichtbarkeitsreglungen, durchdreht. Dein TDBUser ist als Private deklariert, aber du willst es in Public verwenden, als Result für eine Public-Methode/Property. |
AW: Getter wird übergangen
Delphi 2010 steht aber ganz klar im Ausgangspost :zwinker:
|
AW: Getter wird übergangen
Zitat:
Ich habe aber testweise die TDBUsers als ganz eigenständige Klasse gesetzt, was leider noch immer nichts an dem Problem geändert hat. GetDBUserByLogin() wird noch immer geflissentlich gemieden, obwohl explizit auf die zugehörige Property lesend zugegriffen wird :?. Daran lag es leider nicht. (Was heisst leider... das wäre recht dümmlich, wenn sowas nicht in Delphi ginge :)) EDIT: Okay, lustig... Ich habe noch eine weitere Property "Users", die wie "UsersByLogin" arbeitet, aber eben eine ID statt eines Logins nimmt:
Delphi-Quellcode:
TDB = class
private class var FQuery: TUniQuery; class var FUser: TDBUser; class function GetDBUser(aUserID: Integer): TDBUser; static; class function GetDBUserByLogin(aLogin: string): TDBUser; static; public class property Users[aUserID: Integer]: TDBUser read GetDBUser; class property UsersByLogin[aLogin: string]: TDBUser read GetDBUserByLogin; end; implementation class function TDB.GetDBUser(aUserID: Integer): TDBUser; begin FUser.FID := aUserID; result := FUser; end; Folgenden Methode:
Delphi-Quellcode:
Hier wird mir vom Compiler gemeldet, dass der auf "id" zugewiesene Wert nicht mehr verwendet wird! :shock:
procedure TframeUsers.btnResetPasswordClick(Sender: TObject);
var id: Integer; begin id := qryUsers.FieldByName('id').AsInteger; // <---- !!! TDB.Users[id].Password := ''; end; |
AW: Getter wird übergangen
Zitat:
Ist ja auch total sinnlos. So verhinderst du ja, dass man den Rückgabewert in einer Variable speichern kann... Wat'n Schwachsinn... |
AW: Getter wird übergangen
Eventuell schlägt sich auch hier wieder ein Fehler im Compiler nieder?
Versuch es mal so:
Delphi-Quellcode:
Ansonsten wüßte ich auch nicht weiter (jedenfalls nicht ohne mal ein Testprojekt zum Testen zu haben).
TDB = class
public type TDBUser = record private FID: Integer; // für jedes DB feld eben... public property ID: Integer read FID; property Name: string read FName; end; private class var FQuery: TUniQuery; class var FUser: TDBUser; class function GetDBUserByLogin(aLogin: string): TDBUser; static; public abc: Integer; // dummies xyz: String; // class property UsersByLogin[aLogin: string]: TDBUser read GetDBUserByLogin; end; |
Alle Zeitangaben in WEZ +1. Es ist jetzt 10:51 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