AGB  ·  Datenschutz  ·  Impressum  







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

Referzen in ein Stream speichern

Ein Thema von Geri · begonnen am 14. Sep 2006 · letzter Beitrag vom 15. Sep 2006
Antwort Antwort
Seite 2 von 3     12 3      
Geri

Registriert seit: 23. Nov 2004
Ort: Feldkirch
47 Beiträge
 
Delphi 2005 Personal
 
#11

Re: Referzen in ein Stream speichern

  Alt 15. Sep 2006, 00:33
Hallo Hagen

Vielen Dank nochmals für die Darstellung der Situation und Lösungsansatz. Dein Ansatz mit den Nodes sieht auch interessant aus. Wie sieht hier aber bitte das Basisobjekt Tnode aus?


However, mal schauen ob ich her weiter komme... Früher, als es noch die VMT gab, war das jedenfalls noch um einiges einfacher.

Beste Grüsse und herzlichen Dank

Geri
Gerhard
  Mit Zitat antworten Zitat
Benutzerbild von negaH
negaH

Registriert seit: 25. Jun 2003
Ort: Thüringen
2.950 Beiträge
 
#12

Re: Referzen in ein Stream speichern

  Alt 15. Sep 2006, 01:03
Sorry Geri, aber die VMT gibts noch heute, muß es auch denn sonst gäbe es keine Methoden die als "virtual" deklariert sind. Zudem gibts noch die DMT=dynamic Method Table eingeführt in Borland Pascal 7. Und dann noch die RTTI. All das gibts seit mindestens Delphi 1, die DMT seit BP7 und die VMT seit BP5.

Den Source zu meinen TNode kann ich leider nicht veröffentlichen. Ist auch nicht soooo wichtig da es im Grunde auch nur von TObject abgeleitet wurde, eine stinknormale Klasse eben.

Entscheidend ist der Prozess beim Speichern und Laden einer solchen Klasse die nicht von TComponent oder TPersistent abgeleitet wurde. Warum Borland sein VCL Streaming System erst mit TComponent eingebaut hat und warum es keine TObject Klassen abspeichern kann ist mir ebenfalls ein Rätsel.

Auch hätte Borland ohne Probleme auf den Notbehelf mit RegisterClasses() und GetClass() komplett verzichten können. Wie ich hier im Forum schon gezeigt habe (Thread RTII enumerieren in der CodeLib) kann man durchaus über die RTTI aller Module iterieren und somit auch zu einem Klassennamen die Klasse ermitteln. Die Klasse -> TClass, TObject usw. ist nämlich nichts anderes als ein Zeiger in das Codesegement desjenigen Modules das die Unit enthält in dem diese Klasse deklariert wurde und zeigt direkt in die RTTI=Run Time Type Information dieser Klasse. In dieser RTTI enthalten ist dann auch die VMT, DMT, InterfaceTable,Classname,ParentClass,Destructor,Co nstructor,InstanceSize,GUID Tabelle usw. einer Klasse.

Ich meine das du dich näher mit meinem letzten Vorschlag mit .LoadFromStream() -> .ReadData() und .SaveToStream() -> WriteData() beschäftigen solltest. Das funktioniert, ist recht simpel und auch relativ flexibel.

Der einzigste Vorteil des VCL Streamings wäre das man

1.) mit ObjectBinaryToText() einen solchen Stream aus dem binären Format in ein Textformat konvertieren kann, siehe DFMs. Somit ist der Streaminhalt direkt editierbar wenn man weis was man tut. Man kann diesen Text dann auch wieder zurück ins binäre Format konvertieren, was Speicherplatz spart.

2.) in gewissen Grenzen ist das VCL Format updatebar. Dh. man kann zu einem späteren Zeitpunkt in einer existenten Klasse neue Properties deklarieren und denoch alte Stream einlesen ohne Probleme. Diese neuen Properties sind dann einfach auf dem default Wert eingelstellt und werden ansich ignoriert. Das Entfernen einer solchen Property ist aber gefährlich. Denn lädt man ein alten Stream mit dieser alten nun entfernten Property dann krachts. Aber immerhin kann man diese Objekte in Zukunft noch erweitern.

3.) das Streamingsystem enthält alles um alle Standard Datentypen wie Integer, String, TPersistent Klassen die als published Proeprties deklariert wurden, zu speichern und zu laden. Man muß also keinerlei Code mehr schreiben der das macht.

4.) das das Streamingsystem nicht nur den Wert einer Property abspeichert sondern auch den Namen dieser Property (zb. eben X=7 Y=18) ist die Reihenfolge in der die Properties zu einer Klasse im Stream liegen irrelevant.

All diese Punkte waren ein k.o. Kriterium für die Entscheidung das auch in meinen TNode Klassen benutzen zu wollen.

Gruß Hagen
  Mit Zitat antworten Zitat
Geri

Registriert seit: 23. Nov 2004
Ort: Feldkirch
47 Beiträge
 
Delphi 2005 Personal
 
#13

Re: Referzen in ein Stream speichern

  Alt 15. Sep 2006, 02:21
Hallo Hagen

Vielen Dank für Dene detaillierten Erläuterungen zu den Konzepten. Ich habe inschwischen ein kleines Progrämmchen erstellt und denke, ich bin fast am Ziel, aber es funktioniert noch nicht.

Nach dem Drücken des Knopfes werden zuerst 10 Punkte und anschliessend fünf Kanten erzeugt. Beim lesen aus dem Stream scheitert das Programm aber an der Stelle wo:

if myClass = nil then
raise Exception.CreateFmt('Klasse "%" ist nicht registriert.', [Name]);

steht.

Die speziellen Schlüsselwöter dynamic und static habe ich weggelassen.
RegisterClasses([TPoint, TKante]); habe ich auch auskommentiert

Wäre nett von Dir, wenn du bitte mal mit Deinem geschulten Auge den Code ansehen würdest. Es ist ein minimales "lauffähiges" Programm.

Gebe für heute auf, vielen Dank, gute Nachte und beste Grüsse

Geri
Gerhard
  Mit Zitat antworten Zitat
Benutzerbild von negaH
negaH

Registriert seit: 25. Jun 2003
Ort: Thüringen
2.950 Beiträge
 
#14

Re: Referzen in ein Stream speichern

  Alt 15. Sep 2006, 02:35
Wenn du RegisterClasses() weglässt so kann GetClass() nichts finden -> Class == nil;

Das Problem ist doch folgendes:

Eine TClass ist ein Zeiger in das CodeSegement. Da wir aber immer weiter am Source rumbasteln und alles neu kompilieren wird also der Zeiger dieser Klasse ins Codesegement abhängig von unserem Source, dem Compiler dem Linker und sogar vom Betriebstsystem sein das unseren Code in den Speicher lädt. Der Zeiger auf eine Klasse ist also nur zur Laufzeit gültig und kann von Laufzeit zu Laufzeit einen anderen Wert annehmen. Ergo: ein direktes Speichern dieses Klassenzeigers in einen externen Stream fällt flach da er beim nächsten Laden ins Nirwana zeigen könnte (höchst wahrscheinlich).

Wir müssen also einen anderen eindeutigen Wert im Stream abspeichern. Nun Klassennamen müssen in Delphi eindeutig sein, zumindestens innerhalb einer Unit.

Damit wir aber zu einem Klassennamen die Klasse bekommen müssen wir diese Klasse zu Laufzeit irgendwo registrieren (normalerweise wenn wir ohne Tricks arbeiten wollen es geht nämlich auch ohne)

Borland benutzt dazu RegisterClasses(). Alle übergebenen Klassen werden in einer globalen TList gespeichert, wir wissen ja das eine Klasse auch nur ein Pointer ist und somit in eine TList passt.

Sucht man nun mit GetClass(AClassName):TClass in dieser Liste so geht man jeden Eintrag in der List durch und vergleicht mit AnsiCompareText(TClass(List[i]).ClassName, AClassName) diesen Namen. Sollte er übeeinstimmen haben wir zu einem Klassennamen als String die zugehrige Klasse und damit den zur Laufzeit gültigen Zeiger auf die Klasse gefunden.

Falls du die Sourcen von Classes.pas hast solltest du dir mal beide Funktionen anschauen.

Gruß Hagen
  Mit Zitat antworten Zitat
Geri

Registriert seit: 23. Nov 2004
Ort: Feldkirch
47 Beiträge
 
Delphi 2005 Personal
 
#15

Re: Referzen in ein Stream speichern

  Alt 15. Sep 2006, 08:22
Hallo Hagen

Na du bist ein Nachtschwärmer. Vielen Dank für Deine schnelle Rückmeldung. Ich kann das mit den Pointer im Codesegment gut nachvollziehen. Allerdings lässt sich das Programm nicht compilieren, wenn TBaseClass von TObject abgeleitet ist. Den Source habe ich von Classes.pas leider nicht. Ich habe aber gesehen, dass RegisterClasses nur Klassen übernimmt, die von TPersistentClass abegleietet sind. Ich ware aber eben der Meinung, so.

Beste Grüsse und vilen Dank für Deine grosse Mühe!

Geri
Gerhard
  Mit Zitat antworten Zitat
Benutzerbild von negaH
negaH

Registriert seit: 25. Jun 2003
Ort: Thüringen
2.950 Beiträge
 
#16

Re: Referzen in ein Stream speichern

  Alt 15. Sep 2006, 11:01
Hm, leider habe ich nicht alle Sourcezeilen der VCL im Kopf

Drei Möglichkeiten:

1.) leite deine Basisklasse auch von TPersistent ab
TPersistentClass ist deklariert als TPersistentClass = class of TPersistent.
Vorteil! ist das TPersistent schon den Zuweisungsmechanismus gleicher Instanzen integriert hat, also die vrtuellen Methoden .Assign() und .AssignTo(). Das dürftest du für deine TPoint/TKante Objekte ja ebenfals benötigen um zb. ein TPoint Objekt A mit A.Assign(B) auf die Werte von B zu setzen. Damit würdest du also VCL konform bleiben in deinem Design.

2.) kümmer dich nicht um TPersistentClass und caste deine Klassen hart nach TPersistent. Ein "unschöner" weg aber sauber lauffähig bisher. Denn intern ist es RegisterClasses() und GetClass() ziemlich schnuppe was für eine Klasse sie bekommen, hauptsache abgeleitet von TObject.

3.) baue deine eigene RegisterClasses() und GetClasses(). Das habe ich oben ja schon fast vollständig erklärt gehabt -> maximal 15 Zeilen Source sind nötig.

Ich würde in deinem Falle 1.) und 3.) machen.
1.) um mehr VCL konforme Funktionen drinnen zu haben. Ein TPersistent Nachfahre ist zb. auch TBrush, TFont, TPen und kann somit auch als Property einer Komponnete benutzt werden. Das wäre für deine TPoint/TKante Klassen ja eventuell ebenfalls interessant. Und 3.) um einfach von der VCL Fnktionen wegzukommen, unabhängiger und gezielter zu sein. Immerhin teilen sich viele Dutzende von Units die Funktionalität von RegisterClasses()/GetClass() und ein Klassenname muß nur innerhalb einer Unit eindeutig sein. Es ist also durchaus möglich zwei Klassen mit gleichem Namen zu deklarieren und beide in RegisterClasses() registrieren zu wollen. Das geht natürlich nicht und RegisterClasses() würde eine Exception auslösen. Baust du diese Funktionalität selber für deine TMyBaseClass nach so bist du auf der sicheren Seite.

Gruß Hagen

[edit]
ganz vergessen, dein Basistyp solllte inetwa so ausssehen:

Delphi-Quellcode:
type
  TBase = class;
  TBaseClass = class of TBase;

  TPoint = class(TBase)
  TKante = class(TBase)

procedure RegisterMyClasses(const AClasses: array of TBaseClass);
function GetMyClass(const AClassName: String): TBaseClass;
Entscheidend ist TBaseClass = class of TBase. Du definierst also einen Metaklassen-Datentyp um später mit polymorphen Klassentypen arbeiten zu können. Wir können also nicht nur OOP anwenden um Objekt polymorph == typkompatbel zu machen, sondern eben auch mit Klassen. Eine Variable die vom Basistyp her eine Klasse abgeleitet von TBase enthalten soll muß dann als TBaseClass deklariert werden. Schätze das war der Fehler warum du oben Porbleme bekommen hast (Operator nicht anwendbar, oder so) und ich hatte das bisher nicht explizit erwähnt (dachte du weist das )
[/edit]
  Mit Zitat antworten Zitat
Geri

Registriert seit: 23. Nov 2004
Ort: Feldkirch
47 Beiträge
 
Delphi 2005 Personal
 
#17

Re: Referzen in ein Stream speichern

  Alt 15. Sep 2006, 11:46
Hallo Hagen

Vielen Dank für Deine Rückmeldung. Ich hoffe, du hast nach der langen Nach gut geschlafen

Wenn ich den Code betrachte, dann habe ich mehr und mehr das Gefühl, dass ich das in kurzer Zeit ohne lauffähiges Beispiel nicht umsetzen kann. Auf dieser Ebene bin ich eher Anwender und so wie es aussieht, bedarf es für diese Aufgabe scheinbar mehr Background-Wissen.

Was
function GetMyClass(const AClassName: String): TBaseClass; macht das kann ich mir vorstellen. Hier wird ein Klassenname übergeben und ich liefer den Type des entsprechenden Objektes zurück. Also z.B.

Delphi-Quellcode:
function GetMyClass(const AClassName: String): TBaseClass;
Begin
  if AClassName = 'TPOINTthen result:= TPOINT else
  if AClassName = 'TKANTEthen result:= TKANTE else
                                result:=nil;
End;
Bzgl. Ableiten von TPersistent:
Delphi-Quellcode:
type
  TBase = class;
  TBaseClass = class of TBase;

  TPoint = class(TBase)
  TKante = class(TBase)
TBase is ja bei die hier auch nicht von TPersistent abeleitet..?

Bei mir sehen die Klassen aktuell folgendermassen aus.
Delphi-Quellcode:
 type

  TMyBaseClass = class
  public
    Constructor Create();virtual;
    class function LoadFromStream(Stream: TStream):TMyBaseClass; virtual;
    procedure SaveToStream(Stream: TStream);
    procedure Write(Stream: TStream); dynamic;
    procedure Read(Stream: TStream); dynamic;
  end;

  TPoint = class(TMyBaseClass)
            public
              x,y:Integer;
              Constructor Create(mx,my:Integer);overload;virtual;
              Constructor Create();overload;override;
              procedure Write(S:TStream);override;
              procedure Read(S:TStream);override;
            end;

  TKante = class(TMyBaseClass)
            public
              P1,P2:TPoint;
              procedure Write(Stream: TStream);override;
              procedure Read(Stream: TStream);override;
              constructor Create(mP1,mP2:TPoint);overload;virtual;
              Constructor Create();overload; override;
          end;
wie ich nun
Delphi-Quellcode:
  TBase = class;
  TBaseClass = class of TBase;
einbauen muss..???

Bzgl.
procedure RegisterMyClasses(const AClasses: array of TBaseClass); ..ist mit auch nicht ganz klar was die macht.


Tja, ich komme so nicht weiter. Ich schaue mal weiter im Internet nach einem lauffähigen Beispiel. Vielleicht werde ich fündig.

Jedenfalls nochmals vielen Dank, dass du mir hier so nachhaltig weiter geholfen hast! Nun kenne ich mal die Richtung...

Geri
Gerhard
  Mit Zitat antworten Zitat
Benutzerbild von negaH
negaH

Registriert seit: 25. Jun 2003
Ort: Thüringen
2.950 Beiträge
 
#18

Re: Referzen in ein Stream speichern

  Alt 15. Sep 2006, 12:47
Ok, mal ein kleines Beispiel, mehr geht aber wirklich nicht:

Delphi-Quellcode:
type
  TBase = class(TPersistent)
  protected
    procedure ReadData(Stream: TStream); dynamic; abstract;
    procedure WriteData(Stream: TStream); dynamic; abstract;
    procedure Changed; virtual;
  public
    class function LoadFromStream(Stream: TStream): TBase;
    procedure SaveToStream(Stream: TStream);
  end;

  TBaseClass = class of TBase;

  TPoint = class(TBase)
  private
    FX: Integer;
    FY: Integer;
    procedure SetX(Value: Integer);
    procedure SetY(Value: Integer);
  protected
    procedure ReadData(Stream: TStream); override;
    procedure WriteData(Stream: TStream); override;
  public
    constructor Create(APoint: TPoint); reintoduce; overload;
    constructor Create(AX, AY: Integer); reintruduce; overload;
    procedure Assign(Source: TPersistent); override;
  published
    property X: Integer read FX write SetX;
    property Y: Integer read FY write SetY;
  end;
  
  TKante = class(TBase)
  private
    FPoints: array[0..1] of TPoint;
    function GetPoint(Index: Integer): TPoint;
    procedure SetPoint(Index: Integer; Value: TPoint);
  protected
    procedure ReadData(Stream: TStream); override;
    procedure WriteData(Stream: TStream); override;
  public
    constructor Create(AKante: TKante); reintroduce; overload;
    constructor Create(AStart, AStop: TPoint); reintroduce; overload;
    constructor Create(AStartX, AStartY, AStopX, AStopY: Integer); reintroduce; overload;
    destructor Destroy; override;
    procedure Assign(Source: TPersistent); override;
  published
    property Start: TPoint inxex 0 read GetPoint write SetPoint;
    property Stop: TPoint index 1 read GetPoint write SetPoint;
  end;

procedure RegisterBaseClasses(const AClasses: array of TBaseClass);
function GetBaseClass(const AClassName: String): TBaseClass;

procedure SaveList(List: TList; Stream: TStream); // besser ist es eine eigene TList abzuleiten die nur TBaseClass enthalten kann
procedure LoadList(List: TList; Stream: TStream);

implementation

var
  FClassList: TList = nil;

procedure RegisterBaseClasses(const AClasses: array of TBaseClass);
var
  I: Integer;
begin
  Assert(FClassList <> nil);

  for I := Low(AClasses) to High(AClasses) do
    if not (AClasses[I] is TBaseClass) then
      raise Exception.Create('ungültige Klasse in RegisterBaseClasses')
    else
      if GetBaseClass(AClasses[I].ClassName) <> nil then
        raise Exception.CreateFmt('eine Klasse mit Namen "%s" ist schon registriert', [AClasses[I].ClassName])
      else
        FClassList.Add(AClasses[I]);
end;

function GetBaseClass(const AClassName: String): TBaseClass;
var
  I: Integer;
begin
  Assert(FClassList <> nil);

  Result := nil;
  for I := 0 to FClassList.Count -1 do
    if AnsiCompareText(AClassName, TClass(FClassList[I]).ClassName) = 0 then
    begin
      Result := FClassList[I];
      Break;
    end;
end;

procedure SaveList(List: TList; Stream: TStream);
var
  I: Integer;
begin
  for I := 0 to List.Count -1 do
    TBase(List[I]).SaveToStream(Stream);
end;

procedure LoadList(List: TList; Stream: TStream);
begin
  while Stream.Position < Stream.Size do
    List.Add(TBase.LoadFromStream(Stream));
end;


// .TBase
procedure TBase.Changed;
begin
  // hier zb. ein NotifyEvent einbauen
end;

class function TBase.LoadFromStream(Stream: TStream): TBase;
var
  NewClass: TBaseClass;
  NewName: ShortString;
begin
  Stream.Read(NewName[0], 1);
  Stream.Read(NewName[1], Ord(NewName[0]));
  NewClass := GetBaseClass(NewName);
  if NewClass = nil then
    raise Exception.CreateFmt('Klasse "%s" ist nicht registriert', [NewName]);
  Result := NewClass.Create;
  Result.ReadData(Stream);
end;

procedure TBase.SaveToStream(Stream: TStream);
var
  NewName: ShortString;
begin
  NewName := ClassName;
  Stream.Write(NewName[0], Ord(NewName[0]) +1);
  WriteData(Stream);
end;

// .TPoint
procedure TPoint.SetX(Value: Integer);
begin
  if Value <> FX then
  begin
    FX := Value;
    Changed;
  end;
end;

procedure TPoint.SetY(Value: Integer);
begin
  if Value <> FY then
  begin
    FY := Value;
    Changed;
  end;
end;

procedure TPoint.ReadData(Stream: TStream);
begin
  Stream.Read(FX, SizeOf(FX));
  Stream.Read(FY, SizeOf(FY));
end;

procedure TPoint.WriteData(Stream: TStream);
begin
  Stream.Write(FX, SizeOf(FX));
  Stream.Write(FY, SizeOf(FY));
end;

constructor TPoint.Create(APoint: TPoint);
begin
  inherited Create;
  Assign(APoint);
end;

constructor TPoint.Create(AX, AY: Integer);
begin
  inherited Create;
  X := AX;
  Y := AY;
end;

procedure TPoint.Assign(Source: TPersistent);
var
  S: TPoint absolute Source;
begin
  if Source is TPoint then
  begin
    if (FX <> S.FX) or (FY <> S.Y) then
    begin
      FX := S.FX;
      FY := S.FY;
      Changed;
    end;
  end else
    if Source = nil then // bedeutet TPoint(nil) == TPoint(0,0) und ist eine Definitionssache des Programmierers
    begin
      if FX or FY <> 0 then // effizienter! als if (FX <> 0) or (FY <> 0) then
      begin
        FX := 0;
        FY := 0;
        Changed;
      end;
    end else inherited Assign(Source);
end;

// .TKante
function TKante.GetPoint(Index: Integer): TPoint;
begin
  if FPoints[Index] = nil then
    FPoints[Index] := TPoint.Create; // Auto-Allokation beim Zugriff auf Start oder Stop
  Result := FPoints[Index];
end;

procedure TKante.SetPoint(Index: Integer; Value: TPoint);
begin
  GetPoint(Index).Assign(Value); // WICHTIG! niemals ein Object setzen sondern immer dessen EIgenschaften kopieren
end;

procedure TKante.ReadData(Stream: TStream);
begin
// hier gibt es 2 Möglichkeiten

// 1. die TPoint aus Stream als Objekte laden also NEU erzeugen
  FreeAndNil(FPoints[0]);
  FreeAndNil(FPoints[1]);
  FPoints[0] := TPoint.LoadFromStream(Stream) as TPoint;
  FPoints[1] := TPoint.LoadFromStream(Stream) as TPoint;

// 2. nur die Koordinaten der Points laden
  Start.ReadData(Stream);
  Stop.ReadData(Stream);
// Vorteil: wir sparen die beiden String "TPoint" im Stream
// Nachteil: die beien Punkte Start,Stop müssen IMMER vom Typ TPoint sein
// eine TKante mit 3D Koordinaten könnte von TKante abgeleitet sein aber statt TPoint dann TPoint3D benutzen.
// Diese TPoint3D Klasse hätte also X,Y,Z
// Für eine der beiden Methoden musst du dich entscheiden
end;
  
procedure TKante.WriteData(Stream: TStream);
begin
// 1. Methode, Klasse mit Daten
  Start.SaveToStream(Stream);
  Stop.SaveToStream(Stream);
// 2. Methode, nur Daten
  Start.SaveData(Stream);
  Stop.SaveData(Stream);
end;

procedure TKante.Assign(Source: TPersistent);
begin
  if Source is TKante then
  begin
    Start.Assign(TKante(Source).Start);
    Stop.Assign(TKante(Source).Stop);
    Changed;
  end else
    if Source = nil then
    begin
      FreeAndNil(FPoints[0]);
      FreeAndNil(FPoints[1]);
      Changed;
    end else inherited Assign(Source);
end;

constructor TKante.Create(AStart,AStop: TPoint);
begin
  inherited Create;
  Start := AStart;
  Stop := AStop;
end;

constructor TKante.Create(AStartX,AStartY,AStopX,AStopY: Integer);
begin
  inherited Create;
  Start.X := AStartX;
  Start.Y := AStartY;
  Stop.X := AStopX;
  Stop.Y := AStopY;
end;

constructor TKante.Create(AKante: TKante);
begin
  inherited Create;
  Assign(AKante);
end;

destructor TKante.Destroy;
begin
  FreeAndNil(FPoints[0]);
  FreeAndNil(FPoints[1]);
  inherited Destroy;
end;

initialization
  FClassList := TList.Create;
  RegisterBaseClasses([TPoint, TKante]);
finalization
  FreeAndNil(FClassList);
end.
So wie immer keine Gewähr da ich das alles aus dem Stegreif geschrieben habe ohne es jetzt in Delphi zu testen.

Gruß Hagen
  Mit Zitat antworten Zitat
Geri

Registriert seit: 23. Nov 2004
Ort: Feldkirch
47 Beiträge
 
Delphi 2005 Personal
 
#19

Re: Referzen in ein Stream speichern

  Alt 15. Sep 2006, 13:38
Hallo Hagen

Du bist wirklich spitze!! Vielen Dank! Ich glaube, nun kratze ich doch nochmal die Kurve.

Der code liess sich mit zwei Ausnahmen compilieren einmal ein Tippfehler und einmal die Meldung
[Fehler] uHagen2.pas(70): E2015 Operator ist auf diesen Operandentyp nicht anwendbar
im folgenden COde bei "if not (AClasses[I] is TBaseClass) then"

Delphi-Quellcode:

procedure RegisterBaseClasses(const AClasses: array of TBaseClass);
var
  I: Integer;
begin
  Assert(FClassList <> nil);

  for I := Low(AClasses) to High(AClasses) do
   if not (AClasses[I] is TBaseClass) then
      raise Exception.Create('ungültige Klasse in RegisterBaseClasses')
    else
      if GetBaseClass(AClasses[I].ClassName) <> nil then
        raise Exception.CreateFmt('eine Klasse mit Namen "%s" ist schon registriert', [AClasses[I].ClassName])
      else
        FClassList.Add(AClasses[I]);
end;
Deshalb habe ich den Teil ausser "FClassList.Add(AClasses[I]);" ausgeklammert

Dann habe ich eine Beispiel geschrieben. Es werden 10 Punkte und fünf Kanten erzeugt. Das Schreiben und das Lesen der Punkte klappt. Nur sind in der Liste nach dem Auslen 15 Punkte drin. Die restlichen 5 Punkte haben deshalb nicht definierte Werte. Hmmm??

Die Katenliste ist nach dem Laden auch leer. KantenListe.count = 0. Hast du bitte vielleicht noch eine Idee an was es liegen könnte.

Delphi-Quellcode:
procedure TForm6.Button1Click(Sender: TObject);
var i:Byte;
    P:TPoint;
    K:TKante;
Begin
   PunkteListe:=TList.Create;
   KantenListe:=TList.Create;

   // erzeuge 10 zufällige Punkte
   for i:=0 TO 9 Do
   Begin
      P:=TPoint.create();
      P.X:=i;
      P.y:=i;
      memo1.lines.Add('Init Punkt ' + IntToStr(i) + ' ' + IntToStr(P.x) + ' ' + IntToStr(P.y));
      PunkteListe.Add(p);
   End;

   // Erzeuge 4 zufällige Kanten
   For i:=0 TO 4 Do
   Begin
      K:=TKante.Create();
      K.Start:=PunkteListe[Random(9)];
      K.Stop:=PunkteListe[Random(9)];
      memo1.lines.Add('Init Kante ' + IntToStr(i));
      KantenListe.Add(k);
   End;

   SaveToFile('C:\test.txt'); // schreibe Punkte und Katen in ein File
   PunkteListe.Clear;
   KantenListe.Clear;

   LoadFromFile('C:\test.txt'); // lade Punkte und Katen aus einem File
//------------ zeige Punkte --------------------
   For i:=0 TO PunkteListe.count-1 Do
   Begin
      P:=PunkteListe[i];
      memo1.lines.Add('Gelesen Punkt ' + IntToStr(i) + ' ' + IntToStr(P.x) + ' ' + IntToStr(P.y));
   end;

//------------ zeige Kanten --------------------
   For i:=0 TO KantenListe.count-1 Do
   Begin
      K:=KantenListe[i];
      memo1.lines.Add('Gelesen Kante ' + IntToStr(i) + ' ' + IntToStr(K.Start.x) + ' ' + IntToStr(K.Start.y));

  end;

end;
Beste Grüsse und vielen Dank

Geri
PS: Könntest doch mal vieleicht ein Buch über das Thema schreiben... Reich wird man davon nicht, aber es macht auch Spass. Wenn Deine Beiträge alle so lange sind, dann liegt der Inhalt ja schon vor
Gerhard
  Mit Zitat antworten Zitat
Geri

Registriert seit: 23. Nov 2004
Ort: Feldkirch
47 Beiträge
 
Delphi 2005 Personal
 
#20

Re: Referzen in ein Stream speichern

  Alt 15. Sep 2006, 13:47
Hallo Hagen

Ups, ich habe gesehen, dass du noch ein paar Änderungen gemacht hast.

Mit dem neueren Code (reintroduce) erhalte ich gleich zu Beginn die Meldung, dass die Klasse nicht registriert sei und das Programm endet mit einem Laufzeitfehler. Das war zuvor noch etwas besser

Beste Grüsse

Geri
Gerhard
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 2 von 3     12 3      


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 03:07 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