AGB  ·  Datenschutz  ·  Impressum  







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

Generics Type feststellen?

Ein Thema von magnusp · begonnen am 1. Apr 2017 · letzter Beitrag vom 1. Apr 2017
Antwort Antwort
magnusp

Registriert seit: 1. Apr 2017
3 Beiträge
 
#1

Generics Type feststellen?

  Alt 1. Apr 2017, 11:57
Delphi-Version: 10 Berlin
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:
  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;
Das is nur ein aufs wesentliche beschränkter Auszug, denn es scheitert bereits beim LoadFromFile:

Code:
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;
Das selbe würde dann ja auch für SaveToFile nötig sein.

Gibts da eine Lösung oder habe ich den Wurm im Kopf und sehe vor lauter Wald die Bretter nicht?
  Mit Zitat antworten Zitat
Benutzerbild von DeddyH
DeddyH

Registriert seit: 17. Sep 2006
Ort: Barchfeld
27.625 Beiträge
 
Delphi 12 Athens
 
#2

AW: Generics Type feststellen?

  Alt 1. Apr 2017, 12:05
Hallo und Willkommen in der 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: http://stackoverflow.com/questions/8...-generic-class
Detlef
"Ich habe Angst vor dem Tag, an dem die Technologie unsere menschlichen Interaktionen übertrumpft. Die Welt wird eine Generation von Idioten bekommen." (Albert Einstein)
Dieser Tag ist längst gekommen
  Mit Zitat antworten Zitat
magnusp

Registriert seit: 1. Apr 2017
3 Beiträge
 
#3

AW: Generics Type feststellen?

  Alt 1. Apr 2017, 12:24
Hi,
und Danke!

Die Typabfrage geht so schon mal...

allerdings regt sich der Compiler dann bei

Code:
if TypeInfo(T) = TypeInfo(Integer) then
  ...
  fData[i,j] := StrToIntDef(sa[j], 0); //<-E2010
auf mit: E2010 Inkompatible Typen: 'T' und 'Integer'

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?
  Mit Zitat antworten Zitat
Benutzerbild von DeddyH
DeddyH

Registriert seit: 17. Sep 2006
Ort: Barchfeld
27.625 Beiträge
 
Delphi 12 Athens
 
#4

AW: Generics Type feststellen?

  Alt 1. Apr 2017, 13:01
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.
Detlef
"Ich habe Angst vor dem Tag, an dem die Technologie unsere menschlichen Interaktionen übertrumpft. Die Welt wird eine Generation von Idioten bekommen." (Albert Einstein)
Dieser Tag ist längst gekommen
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.184 Beiträge
 
Delphi 12 Athens
 
#5

AW: Generics Type feststellen?

  Alt 1. Apr 2017, 15:31
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.
$2B or not $2B

Geändert von himitsu ( 1. Apr 2017 um 15:35 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Zacherl
Zacherl

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

AW: Generics Type feststellen?

  Alt 1. Apr 2017, 16:29
Hier kannst du eigentlich nur tricksen, oder wenn es nicht allzu performant sein muss, schau dir mal TValue an.

Für so triviale Datentypen kanns du mit etwas arbeiten:
Delphi-Quellcode:
var
  I: Integer;
begin
  if (TypeInfo(T) = TypeInfo(Integer)) then
  begin
    Move(GenericValue, I, SizeOf(T));
oder alternativ:
Delphi-Quellcode:
var
  I: Integer absolute GenericValue;
begin
  if (TypeInfo(T) = TypeInfo(Integer)) then
  begin
    ShowMessage(IntToStr(I));
  end;
Edit:
Achso, es gibt mitlerweile ein paar Intrinsics bezüglich Typ-Checks: http://delphisorcery.blogspot.de/201...n-xe7.html?m=1. Insbesondere GetTypeKind könnte für dich eine Alternative zu TypeInfo 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 case ..of verwenden musste bzw. ein entsprechendes if Statement nur eine Bedinung haben durfte. Müsstest du ggfls. nochmal selbst ausprobieren).
Projekte:
- GitHub (Profil, zyantific)
- zYan Disassembler Engine ( Zydis Online, Zydis GitHub)

Geändert von Zacherl ( 1. Apr 2017 um 16:37 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.184 Beiträge
 
Delphi 12 Athens
 
#7

AW: Generics Type feststellen?

  Alt 1. Apr 2017, 16:44
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.
$2B or not $2B
  Mit Zitat antworten Zitat
Whookie

Registriert seit: 3. Mai 2006
Ort: Graz
445 Beiträge
 
Delphi 10.3 Rio
 
#8

AW: Generics Type feststellen?

  Alt 1. Apr 2017, 19:23
Vielleicht wäre ein Basisklasse mit Generics eine Idee, davon abgeleitet dann die entsprechende Implementierung eines konkreten Typs?
Whookie

Software isn't released ... it is allowed to escape!
  Mit Zitat antworten Zitat
magnusp

Registriert seit: 1. Apr 2017
3 Beiträge
 
#9

AW: Generics Type feststellen?

  Alt 1. Apr 2017, 19:26
Danke für die Hilfe! Falls das mal jemand benötigt hier meine Version (mit ein bischen Generics)

Code:
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;
Die Implementierung sieht dann (ungetestet) wie folgt aus:

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;
  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 19:55 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