|
Antwort |
Registriert seit: 23. Nov 2004 Ort: Feldkirch 47 Beiträge Delphi 2005 Personal |
#11
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
|
Zitat |
Registriert seit: 25. Jun 2003 Ort: Thüringen 2.950 Beiträge |
#12
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 |
Zitat |
Registriert seit: 23. Nov 2004 Ort: Feldkirch 47 Beiträge Delphi 2005 Personal |
#13
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
|
Zitat |
Registriert seit: 25. Jun 2003 Ort: Thüringen 2.950 Beiträge |
#14
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 |
Zitat |
Registriert seit: 23. Nov 2004 Ort: Feldkirch 47 Beiträge Delphi 2005 Personal |
#15
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
|
Zitat |
Registriert seit: 25. Jun 2003 Ort: Thüringen 2.950 Beiträge |
#16
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:
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 )
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; [/edit] |
Zitat |
Registriert seit: 23. Nov 2004 Ort: Feldkirch 47 Beiträge Delphi 2005 Personal |
#17
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:
Bzgl. Ableiten von TPersistent:
function GetMyClass(const AClassName: String): TBaseClass;
Begin if AClassName = 'TPOINT' then result:= TPOINT else if AClassName = 'TKANTE' then result:= TKANTE else result:=nil; End;
Delphi-Quellcode:
TBase is ja bei die hier auch nicht von TPersistent abeleitet..?
type
TBase = class; TBaseClass = class of TBase; TPoint = class(TBase) TKante = class(TBase) Bei mir sehen die Klassen aktuell folgendermassen aus.
Delphi-Quellcode:
wie ich nun
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;
Delphi-Quellcode:
einbauen muss..???
TBase = class;
TBaseClass = class of TBase; 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
|
Zitat |
Registriert seit: 25. Jun 2003 Ort: Thüringen 2.950 Beiträge |
#18
Ok, mal ein kleines Beispiel, mehr geht aber wirklich nicht:
Delphi-Quellcode:
So wie immer keine Gewähr da ich das alles aus dem Stegreif geschrieben habe ohne es jetzt in Delphi zu testen.
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. Gruß Hagen |
Zitat |
Registriert seit: 23. Nov 2004 Ort: Feldkirch 47 Beiträge Delphi 2005 Personal |
#19
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:
Deshalb habe ich den Teil ausser "FClassList.Add(AClasses[I]);" ausgeklammertprocedure 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; 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:
Beste Grüsse und vielen Dank
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; 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
|
Zitat |
Registriert seit: 23. Nov 2004 Ort: Feldkirch 47 Beiträge Delphi 2005 Personal |
#20
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
|
Zitat |
Ansicht |
Linear-Darstellung |
Zur Hybrid-Darstellung wechseln |
Zur Baum-Darstellung wechseln |
ForumregelnEs 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
|
|
Nützliche Links |
Heutige Beiträge |
Sitemap |
Suchen |
Code-Library |
Wer ist online |
Alle Foren als gelesen markieren |
Gehe zu... |
LinkBack |
LinkBack URL |
About LinkBacks |