Einzelnen Beitrag anzeigen

Benutzerbild von Stevie
Stevie

Registriert seit: 12. Aug 2003
Ort: Soest
4.027 Beiträge
 
Delphi 10.1 Berlin Enterprise
 
#17

AW: Generics - Pro und Contra

  Alt 24. Mai 2014, 12:24
Das einzige was mir spontan zu "Contra" eingefallen wäre ist die Tatsache, dass es in Delphi komischerweise immer Generics genannt wird, aber tatsächlich Templates sind.
Ob über Templates wie in C++ oder anders (schlauer) in C# - das ist ein Implementierungsdetail, aber Generics sinds beides.
In Delphi gibt es aber sehr wohl Unterschiede und Gemeinsamkeiten zu beidem

Z.B. wird auch bei leeren Listen in einen Durchlauf "hinein gesprungen", dieser aber dann wieder abgebrochen.
Danach steht der Debugger dann wieder auf der Ausgangszeile.
Das hat mit der Codegenerierung für eine for-in Schleife an sich zu tun und nicht mit Generics.

Dieser Code:
Delphi-Quellcode:
program Project1;

{$APPTYPE CONSOLE}

type
  TEnumerator = class
    fCurrent: Integer;
    function MoveNext: Boolean;
    property Current: Integer read fCurrent;
  end;

  TEnumerable = class
    function GetEnumerator: TEnumerator;
  end;

function TEnumerable.GetEnumerator: TEnumerator;
begin
  Result := TEnumerator.Create;
end;

function TEnumerator.MoveNext: Boolean;
begin
  Inc(fCurrent);
  Result := fCurrent < 10;
end;

var
  e: TEnumerable;
  i: Integer;
begin
  for i in e do
    Writeln(i);
  Readln;
end.
Führt zu diesem Assembler Code in XE6:

Code:
Project9.dpr.36: for i in e do
0041C454 A1BC3E4200       mov eax,[$00423ebc]
0041C459 E876D8FFFF      call TEnumerable.GetEnumerator
0041C45E 8945EC          mov [ebp-$14],eax
0041C461 33C0             xor eax,eax
0041C463 55               push ebp
0041C464 68C5C44100       push $0041c4c5
0041C469 64FF30           push dword ptr fs:[eax]
0041C46C 648920           mov fs:[eax],esp
0041C46F EB25             jmp $0041c496
0041C471 8B45EC          mov eax,[ebp-$14]
0041C474 8B4004           mov eax,[eax+$04]
0041C477 A3C03E4200       mov [$00423ec0],eax
Project9.dpr.37: Writeln(i);
0041C47C A18CE64100       mov eax,[$0041e68c]
0041C481 8B15C03E4200     mov edx,[$00423ec0]
0041C487 E8E08CFEFF      call @Write0Long
0041C48C E8BB8FFEFF      call @WriteLn
0041C491 E8D27BFEFF      call @_IOTest
Project9.dpr.36: for i in e do
0041C496 8B45EC          mov eax,[ebp-$14]
0041C499 E856D8FFFF      call TEnumerator.MoveNext
0041C49E 84C0             test al,al
0041C4A0 75CF            jnz $0041c471
0041C4A2 33C0             xor eax,eax
0041C4A4 5A              pop edx
0041C4A5 59               pop ecx
0041C4A6 59               pop ecx
0041C4A7 648910           mov fs:[eax],edx
0041C4AA 68CCC44100       push $0041c4cc
Project9.dpr.37: Writeln(i);
0041C4AF 837DEC00         cmp dword ptr [ebp-$14],$00
0041C4B3 740F            jz $0041c4c4
0041C4B5 B201             mov dl,$01
0041C4B7 8B45EC          mov eax,[ebp-$14]
0041C4BA 8B08             mov ecx,[eax]
0041C4BC FF51FC          call dword ptr [ecx-$04]
0041C4BF 33C0             xor eax,eax
0041C4C1 8945EC          mov [ebp-$14],eax
0041C4C4 C3               ret
0041C4C5 E90EA0FEFF      jmp @HandleFinally
0041C4CA EBE3             jmp $0041c4af
Wie man sehen kann, stehen da sowohl die Zeile mit der Schleife als auch der einzeilige Rumpf zweimal drin. Daher stoppt der Debugger auch 2mal hintereindner in der Schleifenzeile beim Start (1. GetEnumerator 2. MoveNext) und noch einmal im Rumpf nachdem der letzte Aufruf von MoveNext false geliefert hat um den Enumerator aufzuräumen.

D.h.

Delphi-Quellcode:
Var
  foo : TList<TBar>;
...

Var
  bar : TList<TBar>;
Erzeugt doppelten Code?
Nein, das war mal in Delphi 2009 und 2010(glaube ich) noch so, aber spätestens in XE hat man programmweit nur eine Implementierung für exakt denselben closed generic Type. Allerdings hat du doppelten Code für TList<TBar> und TList<TFoo> obwohl der identisch ist. Übrigens ist type TBarList = TList<TBar>; kein neuer Typ sondern nur ein Alias. In 2009 oder 2010 hätte es das Problem gelöst, glaube ich - aber sicher bin ich da nicht mehr.

Ich persönlich find es gut, dass die Typüberprüfung bereits bei der Definiton des Generics stattfindet. Das hilft Fehler schneller zu finden, als wenn du das erst bei der Spezialisierung mit einem bestimmten Typ hast...
Leider ist das nur die halbe Wahrheit, denn manche Überprüfungen werden erst bei der Spezialisierung durchgeführt (weil ja dann erst das T fest steht). Dann stopt der Compiler nämlich gerne mal an irgendwelchen Zeilen, die überhaupt nix mit dem Fehler zu tun haben.
Stefan
“Simplicity, carried to the extreme, becomes elegance.” Jon Franklin

Delphi Sorcery - DSharp - Spring4D - TestInsight

Geändert von Stevie (24. Mai 2014 um 12:29 Uhr)
  Mit Zitat antworten Zitat