![]() |
Delphi-Version: 10 Berlin
Generics Type feststellen?
Hi!
Tolles Forum habt ihr hier!!! Ich hatte früher mal einwenig mit Delphi herumgespielt und bin jetzt wieder mit der Starter Edition eingestiegen... und stehe gleich mal for dem ersten Problem. Ich wollte so zum Üben ein TCSVFile<T> - Klasse machen mit der man ganzahlige aber auch reelle Dateien lesen/schreiben kann. Daher habe ich dann sowas:
Code:
Das is nur ein aufs wesentliche beschränkter Auszug, denn es scheitert bereits beim LoadFromFile:
TCSVFileExt<T> = Class
private fData: Array Of Array Of T; function GetData(Col, Row: Integer): T; procedure SetData(Col, Row: Integer; const Value: T); public procedure Clear; procedure LoadFromFile(FileName: String); property Data[Col, Row: Integer]: T read GetData write SetData; End;
Code:
Das selbe würde dann ja auch für SaveToFile nötig sein.
procedure TCSVFileExt<T>.LoadFromFile(FileName: String);
var LTmp: TStringList; i, LEnd: Integer; sa: TStringDynArray; j: Integer; LFmtSet: TFormatSettings; begin LTmp := TStringList.Create; try LTmp.LoadFromFile(FileName); TrimData(LTmp); fColCount := CalcColCount(LTmp); fRowCount := LTmp.Count; if (fColCount > 0) And (fRowCount > 0) then begin SetLength(fData, fColCount, fRowCount); for i := 0 to fRowCount-1 do begin sa := SplitString(LTmp[i], ','); LEnd := Min(fColCount, Length(sa)); if (T= Integer) then //<---- geht nicht begin for j := 0 to LEnd-1 do begin fData[i,j] := StrToIntDef(sa[j], 0); end; end else if (T Is Double) Or (T Is Extended) Or (T Is Single) then //<---- geht auch nicht begin LFmtSet := FormatSettings.Create; LFmtSet.DecimalSeparator := '.'; for j := 0 to LEnd-1 do begin fData[i,j] := StrToFloatDef(sa[j], 0.0, LFmtSet); end; end; // more types here... -> else if (T Is Cardinal) then ... else if (T Is Int64) ... UInt64 ... usw. end; end else Clear; finally LTmp.Free; end; end; Gibts da eine Lösung oder habe ich den Wurm im Kopf und sehe vor lauter Wald die Bretter nicht? |
AW: Generics Type feststellen?
Hallo und Willkommen in der DP :dp:,
ich finde zwar, dass das ein klein wenig die Intention generischer Typen konterkariert, aber bei CSV habe ich auch keine bessere Idee. Schau doch mal hier: ![]() |
AW: Generics Type feststellen?
Hi,
und Danke! Die Typabfrage geht so schon mal... allerdings regt sich der Compiler dann bei
Code:
auf mit: E2010 Inkompatible Typen: 'T' und 'Integer'
if TypeInfo(T) = TypeInfo(Integer) then
... fData[i,j] := StrToIntDef(sa[j], 0); //<-E2010 und alle Arten von Typumwandlungen die mir so einfallen bemängelt er auch mit einem Fehler (Ungültige Typumwandlung)... Vielleicht gehe ich das ja komplett falsch an und Generics sind dazu nicht geeignet ... dann ist es wohl besser für jeden Datentyp eine eigene Klasse zu erstellen? |
AW: Generics Type feststellen?
Ich dachte mir schon, dass das schwierig wird. Vielleicht solltest Du an dieser Stelle wirklich besser auf Generics verzichten, aber ich bin da auch nicht der Oberexperte.
|
AW: Generics Type feststellen?
IS geht natürlich nur für Klassen.
Und IS kann man im Generic auch nur benutzten, wenn der generische Typ auf Klassen eingeschränkt wird. In Generics wird der Code bereits beim Parsen validiert und man kann da nur etwas nutzen, was die Vorgaben erlauben. Bei einem offenen generischen Typen geht fast garnichts, also kann Integer niemals an den Typen zugewiesen werden, da zu diesem Zeitpunkt das ALLES sein könnte, da der "<Integer>" noch nicht bekannt ist. Es wäre schlauer gewesen, wenn nur syntaktisch geprüft wird, aber in der Definition noch keinerlei Typprüfungen durchgeführt würden (außer wenn der Typ vorher eingeschränkt wurde und wenn es grudsätzlich gegen dieser Vorgabe verstößt). Aber Embarcadero war leider nicht schlau, damals. Daher sind Generics für viele Dinge einfach nicht nutzbar und mit den Makros aus C-Sprachen absolut nicht konkurrieren. |
AW: Generics Type feststellen?
Hier kannst du eigentlich nur tricksen, oder wenn es nicht allzu performant sein muss, schau dir mal
Delphi-Quellcode:
an.
TValue
Für so triviale Datentypen kanns du mit etwas arbeiten:
Delphi-Quellcode:
oder alternativ:
var
I: Integer; begin if (TypeInfo(T) = TypeInfo(Integer)) then begin Move(GenericValue, I, SizeOf(T));
Delphi-Quellcode:
Edit:
var
I: Integer absolute GenericValue; begin if (TypeInfo(T) = TypeInfo(Integer)) then begin ShowMessage(IntToStr(I)); end; Achso, es gibt mitlerweile ein paar Intrinsics bezüglich Typ-Checks: ![]()
Delphi-Quellcode:
könnte für dich eine Alternative zu
GetTypeKind
Delphi-Quellcode:
sein, da der Compiler hier mitlerweile sogar so klug ist und tatsächlich nur die verwendeten Codeteile in das fertige Binary linkt (gab da meine ich irgendeine Einschränkung, dass man entweder
TypeInfo
Delphi-Quellcode:
..
case
Delphi-Quellcode:
verwenden musste bzw. ein entsprechendes
of
Delphi-Quellcode:
Statement nur eine Bedinung haben durfte. Müsstest du ggfls. nochmal selbst ausprobieren).
if
|
AW: Generics Type feststellen?
Besser CopyRecord bzw. CopyArray (mit Länge 1) aus der System-Unit, denn wenn T ein String wäre, dann bereitet dieses Move einige Problemchen.
Alternativ über TValue der RTTI auslesen&zuweisen. Aber in diesem Fall einfach über PInteger casten und die Integer zuweisen, anstatt des Move. |
AW: Generics Type feststellen?
Vielleicht wäre ein Basisklasse mit Generics eine Idee, davon abgeleitet dann die entsprechende Implementierung eines konkreten Typs?
|
AW: Generics Type feststellen?
:thumb: Danke für die Hilfe! Falls das mal jemand benötigt hier meine Version (mit ein bischen Generics)
Code:
Die Implementierung sieht dann (ungetestet) wie folgt aus:
Type
TCustomCSVFile<T> = Class private fRowCount: Integer; fColCount: Integer; fData: Array Of Array Of T; function GetData(ACol, ARow: Integer): T; procedure SetData(ACol, ARow: Integer; const Value: T); protected procedure TrimData(AData: TStringList); function CalcColCount(AData: TStringList): Integer; procedure SetDataRow(ARow, MaxCol: Integer; const Data: TStringDynArray); virtual; abstract; function GetDataRow(ARow: Integer): String; virtual; abstract; public procedure Clear; virtual; abstract; procedure LoadFromFile(FileName: String); procedure SaveToFie(FileName: String); procedure DimData(ColCount, RowCount: Integer); property RowCount: Integer read fRowCount; property ColCount: Integer read fColCount; property Data[ACol, ARow: Integer]: T read GetData write SetData; End; TDoubleCSVFile = Class(TCustomCSVFile<Double>) protected procedure SetDataRow(ARow, MaxCol: Integer; const Data: TStringDynArray); override; function GetDataRow(ARow: Integer): String; override; End; TIntegerCSVFile = Class(TCustomCSVFile<Integer>) protected procedure SetDataRow(ARow, MaxCol: Integer; const Data: TStringDynArray); override; function GetDataRow(ARow: Integer): String; override; End;
Code:
{ TCustomCSVFile } procedure TCustomCSVFile<T>.DimData(ColCount, RowCount: Integer); begin SetLength(fData, ColCount, RowCount); end; procedure TCustomCSVFile<T>.LoadFromFile(FileName: String); var LTmp: TStringList; i, LEnd: Integer; sa: TStringDynArray; j: Integer; begin LTmp := TStringList.Create; try LTmp.LoadFromFile(FileName); TrimData(LTmp); fColCount := CalcColCount(LTmp); fRowCount := LTmp.Count; if (fColCount > 0) And (fRowCount > 0) then begin DimData(fColCount, fRowCount); for i := 0 to fRowCount-1 do begin sa := SplitString(LTmp[i], ','); LEnd := Min(fColCount, Length(sa)); SetDataRow(i, LEnd, sa); end; end else Clear; finally LTmp.Free; end; end; procedure TCustomCSVFile<T>.SaveToFie(FileName: String); var LTmp: TStringList; i, j: Integer; begin LTmp := TStringList.Create; try for i := 0 to Length(fData)-1 do begin LTmp.Add(GetDataRow(i)); end; LTmp.SaveToFile(FileName); finally LTmp.Free; end; end; function TCustomCSVFile<T>.GetData(ACol, ARow: Integer): T; begin Result := fData[ACol, ARow]; end; procedure TCustomCSVFile<T>.SetData(ACol, ARow: Integer; const Value: T); begin fData[ACol, ARow] := Value; end; function TCustomCSVFile<T>.CalcColCount(AData: TStringList): Integer; var sa: TStringDynArray; begin // use the first row (after TrimData) sa := SplitString(AData[0], ','); Result := Length(sa); end; procedure TCustomCSVFile<T>.TrimData(AData: TStringList); var i: Integer; Ln: string; begin for i := AData.Count-1 downto 0 do begin Ln := Trim(AData[i]); if (Length(Ln) > 0) And (((Ln[1] >= '0') And (Ln[1]<='9')) Or (Ln[1]='+') Or (Ln[1]='-')) then begin AData[i] := Ln; end else AData.Delete(i); end; end; { TDoubleCSVFile } function TDoubleCSVFile.GetDataRow(ARow: Integer): String; var i: Integer; sa: TStringDynArray; LFmtSet: TFormatSettings; begin LFmtSet := TFormatSettings.Create(); LFmtSet.DecimalSeparator := '.'; SetLength(sa, High(fData[ARow])); for i := 0 to High(fData[ARow]) do begin sa[i] := FloatToStr(fData[ARow, i], LFmtSet); end; Result.Join(',', sa); end; procedure TDoubleCSVFile.SetDataRow(ARow, MaxCol: Integer; const Data: TStringDynArray); var i: Integer; LFmtSet: TFormatSettings; begin LFmtSet := TFormatSettings.Create(); LFmtSet.DecimalSeparator := '.'; for i := 0 to MaxCol-1 do begin fData[ARow, i] := StrToFloatDef(Data[i], 0.0, LFmtSet ) end; end; { TIntegerCSVFile } function TIntegerCSVFile.GetDataRow(ARow: Integer): String; var i: Integer; sa: TStringDynArray; begin for i := 0 to High(fData[ARow]) do begin sa[i] := IntToStr(fData[ARow, i]); end; Result.Join(',', sa); end; procedure TIntegerCSVFile.SetDataRow(ARow, MaxCol: Integer; const Data: TStringDynArray); var i: Integer; begin for i := 0 to MaxCol-1 do begin fData[ARow, i] := StrToIntDef(Data[i], 0); end; end; |
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:26 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-2025 by Thomas Breitkreuz