AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Property für Anwender READONLY, intern nicht

Ein Thema von scrat1979 · begonnen am 15. Sep 2018 · letzter Beitrag vom 17. Sep 2018
Antwort Antwort
Benutzerbild von scrat1979
scrat1979

Registriert seit: 12. Jan 2007
Ort: Sulzbach a.d. Murr
1.029 Beiträge
 
Delphi 10.4 Sydney
 
#1

Property für Anwender READONLY, intern nicht

  Alt 15. Sep 2018, 12:17
Hallo zusammen,

ich habe gerade ein Problem bezüglich meiner selbst entwickelten Komponente. Die wichtigsten Klassen-Eigenschaften und -Methoden findet ihr anschließend. Zusammengefasst handelt es sich um einen TCP-Server mit einer Userliste. Der TCP-Server (Komponente) muss INTERN einige Dinge in der User-Klasse zuweisen (z.B. LastLogin). Auf der anderen Seite soll der Benutzer der Komponente "von außen" auf die UserListe zugreifen können. Hier soll jedoch eine Veränderung der Eigenschaften NICHT möglich sein. Um beim genannten Beispiel zu bleiben soll es also von außen NICHT möglich sein, die Property LastLogin zu schreiben sondern ausschließlich zu lesen.

Meine Lösungsidee: Ich erzeuge im Getter eine "ReadOnly"-Version der entsprechenden TExtCientInfo und gebe diese dann über den Getter zurück. Dazu würde ich eine neue private Variable im Server (z.B. FReadOnlyClientInfo) erstellen und diese im Getter entsprechend bestücken und zurückgeben. Diesbezüglich wäre es vielleicht interessant zu wissen, dass auf diese Variable immer nur EIN Lesezugriff gleichzeitig erfolgen wird - mehrere Threads kommen sich dabei definitv nicht in die Quere. Ist dieser Ansatz korrekt?

Anbei ein Auszug aus dem Klassendesign. Sollte noch was fehlen werde ich das natürlich nachreichen.

TCP-Server:

Delphi-Quellcode:
type TMyTCPServer = class(TComponent)
private
 function FGetClient(Idx : Integer) : TExtClientInfo; // Hier müsste eine ReadOnly-Version von TExtClientInfo zurückgegeben werden!
 [...]
public
 [...]
 property Clients[Idx : Integer] : TExtClientInfo read FGetClient;
 [...]
end;

[...]

function TMyTCPServer.FGetclient(Idx : Integer) : TExtClientInfo;
begin
 Result := FClientList[Idx];
end;
Auszug aus TExtClientInfo

Delphi-Quellcode:
type TExtClientInfo = class(TBasicClientInfo)
  private

  [...]

  public
    [...]
    // Die Basisinfos (ClientName, ID etc. finden sich in der Basisklasse)
    
    property Active : Boolean read FActive;

    // Die Properties müssen nach außen hin read-only sein...
    property LastPing : TDateTime read FLastPing write FSetLastPing;
    property LastLogin : TDateTime read FLastLogin write FSetLastLogin;
    property GUID : String read FGUID write FSetGUID;
    property LastActivity : TDateTime read FLastActivity write FSetLastActivity;
    [...]
end;
Besten Dank für Eure Mühe mir zu helfen,
Michael Kübler
  Mit Zitat antworten Zitat
Hobbycoder

Registriert seit: 22. Feb 2017
961 Beiträge
 
#2

AW: Property für Anwender READONLY, intern nicht

  Alt 15. Sep 2018, 13:29
Ich weiß nicht ob ich dich richtig verstanden habe, aber lass doch einfach den Setter  write FSetLastLogin; weg. Dann ist die Property ReadOnly.
Gruß Hobbycoder
Alle sagten: "Das geht nicht.". Dann kam einer, der wusste das nicht, und hat's einfach gemacht.
  Mit Zitat antworten Zitat
Benutzerbild von Zacherl
Zacherl

Registriert seit: 3. Sep 2004
4.629 Beiträge
 
Delphi 10.2 Tokyo Starter
 
#3

AW: Property für Anwender READONLY, intern nicht

  Alt 15. Sep 2018, 15:38
Ich weiß nicht ob ich dich richtig verstanden habe, aber lass doch einfach den Setter  write FSetLastLogin; weg. Dann ist die Property ReadOnly.
Ne, das Problem ist das Gleiche, wie wenn man z.B. eine TList<T> Property hat. Dann kann man zwar das Ersetzen der Instanz verhindern, wenn man write nicht angibt, aber ein Aufruf von z.B. List.Delete(i) ist dennoch möglich.

@TE: Soweit ich weiß gibt es da in Delphi keine "saubere" Methode. Ein read-only Proxy Objekt wäre wohl auch mein Ansatz; auch wenn es unnötiger Overhead ist. Am elegantesten wäre wohl noch ein Interface, was du einmal von der "richtigen" Klasse und einmal vom Proxy implementierst.
Projekte:
- GitHub (Profil, zyantific)
- zYan Disassembler Engine ( Zydis Online, Zydis GitHub)
  Mit Zitat antworten Zitat
Rollo62

Registriert seit: 15. Mär 2007
4.116 Beiträge
 
Delphi 12 Athens
 
#4

AW: Property für Anwender READONLY, intern nicht

  Alt 15. Sep 2018, 17:10
Könntest du mit ReadOnly Interfaces arbeiten ?
  Mit Zitat antworten Zitat
Hobbycoder

Registriert seit: 22. Feb 2017
961 Beiträge
 
#5

AW: Property für Anwender READONLY, intern nicht

  Alt 15. Sep 2018, 18:28
Ich weiß nicht ob ich dich richtig verstanden habe, aber lass doch einfach den Setter  write FSetLastLogin; weg. Dann ist die Property ReadOnly.
Ne, das Problem ist das Gleiche, wie wenn man z.B. eine TList<T> Property hat. Dann kann man zwar das Ersetzen der Instanz verhindern, wenn man write nicht angibt, aber ein Aufruf von z.B. List.Delete(i) ist dennoch möglich.
Stimmt. Bei nochmaligem Durchlesen habe ich gemerkt, dass ich das tatsächlich überlesen hatte . Ich war fälschlicherweise davon ausgegangen, das es sich um ein direktes Property der Komponente handelt.
Gruß Hobbycoder
Alle sagten: "Das geht nicht.". Dann kam einer, der wusste das nicht, und hat's einfach gemacht.
  Mit Zitat antworten Zitat
Benutzerbild von scrat1979
scrat1979

Registriert seit: 12. Jan 2007
Ort: Sulzbach a.d. Murr
1.029 Beiträge
 
Delphi 10.4 Sydney
 
#6

AW: Property für Anwender READONLY, intern nicht

  Alt 15. Sep 2018, 20:11
Vielen Dank für eure Antworten. Die Sache mit dem Interface hört sich interessant an. Ich habe mich schon etwas mit Interfaces beschäftigt und verstehe sämtliche Turorials und kann sie auch entsprechend umsetzen.

Könnt ihr mir dennoch mit paar Zeilen (Pseudo)code zeigen wir man das umsetzt? Das ist mir momentan nicht ganz klar... ich meinte insbesondere die Trennung von ReadOnly und beschreibbar.

Besten Dank bisher!!!
Michael Kübler
  Mit Zitat antworten Zitat
Benutzerbild von scrat1979
scrat1979

Registriert seit: 12. Jan 2007
Ort: Sulzbach a.d. Murr
1.029 Beiträge
 
Delphi 10.4 Sydney
 
#7

AW: Property für Anwender READONLY, intern nicht

  Alt 15. Sep 2018, 20:12
Ich weiß nicht ob ich dich richtig verstanden habe, aber lass doch einfach den Setter  write FSetLastLogin; weg. Dann ist die Property ReadOnly.
Ne, das Problem ist das Gleiche, wie wenn man z.B. eine TList<T> Property hat. Dann kann man zwar das Ersetzen der Instanz verhindern, wenn man write nicht angibt, aber ein Aufruf von z.B. List.Delete(i) ist dennoch möglich.
Stimmt. Bei nochmaligem Durchlesen habe ich gemerkt, dass ich das tatsächlich überlesen hatte . Ich war fälschlicherweise davon ausgegangen, das es sich um ein direktes Property der Komponente handelt.
Jupp.... genau DAS ist der Knackpunkt
Michael Kübler
  Mit Zitat antworten Zitat
Benutzerbild von Codehunter
Codehunter

Registriert seit: 3. Jun 2003
Ort: Thüringen
2.272 Beiträge
 
Delphi 10.4 Sydney
 
#8

AW: Property für Anwender READONLY, intern nicht

  Alt 15. Sep 2018, 20:15
Das geht nur wenn deine List-Klasse in der selben Unit überschrieben wird und deren Methoden wie Delete() umgebogen werden. Dann kannst du dort auf Protected- und Private-Deklarationen der List-Klasse auch aus der TCP-Klasse aus zugreifen. Außerhalb dieser Unit aber nicht.

Besser wäre es wohl aber, deiner Klasse eine indizierte Getter-Property zu verpassen.

Allgemein halte ich das Konzept aber für irrig. Denn meiner Erfahrung nach kann man sich auf den Kopf stellen, wenn der Anwender etwas nicht darf sucht er sich seine Wege oder sucht sich eine andere Lösung. Fange Fehlersituationen durch ungeplante externe Löschungen intern durch Exceptions und/oder Events ab.
Ich mache grundsätzlich keine Screenshots. Schießen auf Bildschirme gibt nämlich hässliche Pixelfehler und schadet der Gesundheit vom Kollegen gegenüber. I und E zu vertauschen hätte den selben negativen Effekt, würde aber eher dem Betriebsklima schaden
  Mit Zitat antworten Zitat
Rollo62

Registriert seit: 15. Mär 2007
4.116 Beiträge
 
Delphi 12 Athens
 
#9

AW: Property für Anwender READONLY, intern nicht

  Alt 17. Sep 2018, 06:32
Ich meine das ungefähr so, nur über Interface zugreifen, und das ReadOnly-Interface bietet dann gar keine Schreibmethoden an.

Delphi-Quellcode:
 ITcpServer = interface
 // Full access
 ....


 ITcpServerReadOnly = interface
 // Restricted access
 ....


 TTcpServer = class(TTcpServerBase, ITcpServer, ITcpServerReadOnly)
  private
  ...
Du kannst auch mal in den Spring4D Sourcen schauen, da gibt es auch ein paar schöne Beispiele zu ReadOnly Interfaces, z.B. ReadOnlyList.

Rollo
  Mit Zitat antworten Zitat
Schokohase
(Gast)

n/a Beiträge
 
#10

AW: Property für Anwender READONLY, intern nicht

  Alt 17. Sep 2018, 07:46
Man kann ein Interface nehmen, muss es aber in diesem Fall doch gar nicht.

Delphi-Quellcode:
TFoo = class
private
  FName: atring;
protected
  function GetName: string; virtual;
  procedure SetName( const Value: string ); virtual;
public
  property Name: string read GetName write SetName;
end;

TReadOnlyFoo = class(TFoo)
private
  FFoo: TFoo;
protected
  function GetName: string; override;
  procedure SetName( const Value: string ); override;
public
  constructor Create( const AFoo: TFoo );
end;

function TReadOnlyFoo.GetName: string;
begin
  Result := FFoo.Name;
end;

procedure TReadOnlyFoo.SetName( const Value: string );
begin
  raise EInvalidOperation.Create( 'Readonly' );
end;
oder hier als ausführliches Beispiel

Delphi-Quellcode:
program ReadOnlyClassProp;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  System.SysUtils,
  System.Classes,
  ReadOnlyClassProp.Types in 'ReadOnlyClassProp.Types.pas';

procedure TestRun;
var
  b: TBar;
begin
  b := TBar.Create( );
  try
    b.ExecuteAction( );
    WriteLn( b.Foo.SomeValue );

    try
      b.Foo.SomeValue := 'Test the readonly Setter';
    except
      on E: EInvalidOperation do; // eat the expected exception
    end;

    WriteLn( b.Foo.SomeValue );

  finally
    b.Free;
  end;
end;

begin
  try
    TestRun;
  except
    on E: Exception do
      WriteLn( E.ClassName, ': ', E.Message );
  end;
  ReadLn;

end.
Delphi-Quellcode:
unit ReadOnlyClassProp.Types;

interface

uses
  System.Classes,
  System.SysUtils;

type
  TFoo = class
  strict private
    FSomeValue: string;
  strict protected
    function GetIsReadOnly: Boolean; virtual;
    function GetSomeValue: string; virtual;
    procedure SetSomeValue( const Value: string ); virtual;
  public
    property IsReadOnly: Boolean read GetIsReadOnly;
    property SomeValue: string read GetSomeValue write SetSomeValue;
  end;

  TReadOnlyFoo = class( TFoo )
  strict private
    FFoo: TFoo;
  strict protected
    function GetIsReadOnly: Boolean; override;
    function GetSomeValue: string; override;
    procedure SetSomeValue( const Value: string ); override;
  public
    constructor Create( const AFoo: TFoo );
  end;

  TBar = class
  private
    FFoo: TFoo;
    FInternalFoo: TFoo;
    procedure SetInternalFoo( const Value: TFoo );
  protected
    property InternalFoo: TFoo read FInternalFoo write SetInternalFoo;
  public
    constructor Create;
    destructor Destroy; override;

    property Foo: TFoo read FFoo;

    procedure ExecuteAction( );
  end;

implementation

{ TFoo }

function TFoo.GetIsReadOnly: Boolean;
begin
  Result := False;
end;

function TFoo.GetSomeValue: string;
begin
  Result := FSomeValue;
end;

procedure TFoo.SetSomeValue( const Value: string );
begin
  FSomeValue := Value;
end;

{ TReadOnlyFoo }

constructor TReadOnlyFoo.Create( const AFoo: TFoo );
begin
  inherited Create( );
  if not Assigned( AFoo )
  then
    raise EArgumentNilException.Create( 'AFoo' );

  FFoo := AFoo;
end;

function TReadOnlyFoo.GetIsReadOnly: Boolean;
begin
  Result := True;
end;

function TReadOnlyFoo.GetSomeValue: string;
begin
  Result := FFoo.SomeValue;
end;

procedure TReadOnlyFoo.SetSomeValue( const Value: string );
begin
  raise EInvalidOperation.Create( 'Readonly' );
end;

{ TBar }

constructor TBar.Create;
begin
  inherited;
  InternalFoo := TFoo.Create;
end;

destructor TBar.Destroy;
begin
  InternalFoo := nil;
  inherited;
end;

procedure TBar.ExecuteAction;
begin
  InternalFoo.SomeValue := DateTimeToStr( Now( ) );
end;

procedure TBar.SetInternalFoo( const Value: TFoo );
begin
  FreeAndNil( FInternalFoo );
  FreeAndNil( FFoo );

  if Value <> nil
  then
    begin
      FInternalFoo := Value;
      FFoo := TReadOnlyFoo.Create( Value );
    end;
end;

end.
Aber threadsafe ist das so noch nicht, das müsste man in TBar dann noch einweben.

Geändert von Schokohase (17. Sep 2018 um 08:17 Uhr)
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 17:35 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz