Category: Delphi

  • Pekare

    En pekare håller reda på ett objekts plats i minnet. Otypade pekare deklareras med typen Pointer, medan typade pekare deklareras med inledande caret (^) vid typnamnet. I detta exempel är a en integer och b en integer-pekare.

    var
      a : integer;
      b : ^integer;

    Värdet som lagras i en pekare är minnesadressen till ett objekt. Variabeln b kan alltså hålla minnesadressen till en integer. För att få minnesadressen till ett objekt, används @. Detta exempel tilldelar adressen av a till b.

    a := 10;
    b := @a;

    Om man försöker läsa av b, får man adressen till a. Syftet med att läsa av b kan t.ex. vara att låta en ny pekare peka på a. Om man istället vill läsa av värdet som finns i variabeln som b pekar på, används caret som postfix. Detta ger svaret 10, eftersom b hänvisar till a som innehåller värdet 10.

    writeln(b^);

    Om ett värde tilldelas till b^ lagras det i a, eftersom b pekar på a.

    b^ := 50;
    writeln(a);

    Funktionspekare benämns procedural types. Variabeln måste vara deklarerad med exakt samma signatur (parametrar och retur) som funktionen den pekar på. Följande exempel använder funktionen sum i en unit som heter Unit1.

    unit Unit1;
    
    interface
    
      function sum(a, b: integer): integer;
    
    implementation
    
    function sum(a, b: integer): integer;
    begin
      result := a + b;
    end;
    
    end.

    En pekare som kan peka på funktionen sum måste ta två integers och ge en integer. Detta exempel deklarerar en kompatibel pekare (s), samt anropar funktionen sum direkt, och via pekaren.

    var
      s : function(x, y : integer) : integer;
    begin
      writeln(sum(10, 20));
      s := sum;
      writeln(s(20, 30));

    För tydlighetens skull, kan man uttryckligen skriva att man läser av adressen till sum, genom att använda prefixet @.

    s := @sum;

    Om funktionen sum istället vore en medlemsfunktion, behöver pekaren både hålla adressen till objektet och till medlemsfunktionen. Denna förändring i Unit1 placerar funktionen sum i klassen test.

    unit Unit1;
    
    interface
    
    type
      test = class
      public
        function sum(a, b: integer): integer;
      end;
    
    implementation
    
    { test }
    
    function test.sum(a, b: integer): integer;
    begin
      result := a + b;
    end;
    
    end.

    Nu deklareras pekaren med tillägget of object.

    var
      t : test;
      s : function(x, y : integer) : integer of object;
    begin
      writeln(t.sum(10, 20));
      s := t.sum;
      writeln(s(20, 30));

    För att skicka en funktion som parameter, måste funktionen deklareras som en typ. Så här ser en typ som beskriver funktionen sum ut:

    type
      sumtype = function(a, b : integer): integer of object;

    Med denna typ på plats, kan deklarationen av pekaren kortas ner från detta:

    s : function(x, y : integer) : integer of object;

    …till detta:

    s : sumtype;

    Typen sumtype kan användas som parametertyp, så att funktionen sum kan skickas som parameter.

    function call_sum(x: sumtype): integer;
    begin
      result := x(10, 20) + x(30, 40);
    end;

    Ett anrop kan se ut så här:

    var
      t : test;
      s : sumtype;
    begin
      s := t.sum;
      writeln(call_sum(s));
  • Tester och iterationer

    If

    If-satser innehåller endast ett semikolon efter att hela satsen är slut. Detta exempel visar en test som alltid utvärderar till false:

      if false then
        writeln('Sant');

    Semikolon ska förekomma efter sista satsen. Detta exempel visar en if-sats med en else-sats:

      if false then
        writeln('Sant')
      else
        writeln('Falskt');

    Och detta visar två stycken else if-satser:

      if false then
        writeln('Sant')
      else if false then
        writeln('Annars falskt')
      else if true then
        writeln('Annars sant')
      else
        writeln('Falskt');

    Om fler än en rad ska exekvera när ett uttryck utvärderas till true, ska dessa kapslas in mellan begin och end.

      if false then
        writeln('Sant')
      else if true then
        begin
          writeln('Rad 1');
          writeln('Rad 2');
        end
      else if false then
        writeln('...')
      else
        writeln('Falskt');

    Om begin … end-blocket hade legat sist, hade end avslutats med semikolon.

    Case

    I ett case-block används alltid semikolon efter satserna, och således även semikolon efter end om man vill skapa ett block. Det finns ingen fall through, så delarna i case-blocket behöver inget kommando för att avbryta exekvering.

      i := 3;
      case i of
        1:
          writeln('1');
        2:
          writeln('2');
        3:
          begin
            writeln('3 a');
            writeln('3 b');
          end;
      end;

    For

    Den grundläggande syntaxen för for påverkar endast en rad:

      for i := 0 to 9 do
        writeln(i);

    För att flera rader ska ingå i iterationen används begin … end med semikolon.

      for i := 0 to 9 do
      begin
        writeln(i);
        writeln(i);
      end;

    Efter att uppräkningen gjorts har i värdet 10. Det går även att använda t.ex. enumereringar i for, vilket illustreras här:

    uses
      System.SysUtils, TypInfo;
    type
      Räkneord = (Första, Andra, Tredje, Fjärde);
    var
      i : Räkneord;
    begin
      for i := Första to Fjärde do
        writeln(GetEnumName(TypeInfo(Räkneord), integer(i)));

    Repeat

    För 1-till-flera används repeat. Iterationen pågår tills uttrycket efter until utvärderas till true.

      i := 1;
      repeat
        writeln(i);
        inc(i);
      until i >= 10;

    While

    För 0-till-flera används while. Till skillnad från repeat så gäller while bara för efterföljande rad såvida inte ett begin … end-block skapas.

      i := 1;
      while i < 10 do
      begin
        writeln(i);
        inc(i);
      end;

    Både for, repeat och while kan avbrytas med break.

  • Introduktion till Delphi XE5

    Språket Delphi är inte skiftlägeskänsligt för nyckelord eller namn. Kompilatorn kontrollerar att konstanta värden får plats i variabeln de tilldelas till, men under körning kontrolleras inte överflöden.

    Viktiga snabbtangenter:

    • F5: Breakpoint
    • F9: Kompilera och starta (Shift+Ctrl+F9 startar utan debugging)
    • Ctrl+F9: Kompilera
    • Ctrl+Shit+C: Implementera alla funktioner

    Unit

    En källkodsfil har filändelsen .pas och benämns som en unit (enhet). En enhet inleds med nyckelordet unit och avslutas med end. (inklusive punkten). Den minimala strukturen ser ut så här:

    unit MinUnit;
    interface
    implementation
    end.

    I sektionen interface anges vilka andra enheter som enheten använder.

    interface
    uses
      MinAndraUnit;

    Dessutom deklareras typer (nyckelordet type) och globala variabler i interface. Sektionen implementation används för att implementera funktioner.

    Datatyper

    Numeriska datatyper:

    • Byte: 0 – 255
    • ShortInt: -127 – 127
    • Word: 16 bit osignerad (0 – 65535)
    • SmallInt: 16 bit signerad (-32768 – 32767)
    • LongWord (eller Cardinal): 32 bit osignerad
    • LongInt (eller Integer): 32 bit signerad
    • Int64: 64 bit signerad
    • Currency: 50 tecken, fyra decimaler

    Flyttal:

    • Single: 7 tecken
    • Double: 15 tecken
    • Extended: 19 tecken

    Konstanter skapas utan att någon datatyp behöver anges. Notera att = vid deklaration av konstanter. Följande exempel skapar en ShortInt (A), en SmallInt (B) och en Integer (C):

    const
      A = 30;
      B = 300;
      C = 300000;

    Text:

    • Char (eller AnsiChar): Ett enkelt tecken
    • WideChar: Ett internationellt tecken
    • ShortString: Upp till 255 Char
    • String (eller AnsiString): Fritt antal Char
    • WideString: Fritt antal WideChar

    Som strängavgränsare används apostrof (‘).

    var
       s : string;
    begin
       s := 'Conny Karlsson';
       writeln(copy(s, 1, 3)); //Första tecknet och tre framåt ger 'Con'
       insert('Helmer ', s, 7); //Infoga 'Helmer ' efter 'Conny '
       writeln(s); //'Conny Helmer Karlsson'
       //Byt ut Helmer till Holger.
       writeln(stringreplace(s, 'Helmer', 'Holger', [rfReplaceAll, rfIgnoreCase]));

    Operatörer

    Aritmetiska operatörer:

    • + (addition eller strängkonkatenering)
    • – (subtraktion)
    • * (multiplikation)
    • / (division)
    • div (heltalsdivision)
    • mod (modulus)

    Logiska operatörer:

    • and (logiskt och)
    • or (logiskt eller)
    • xor (logiskt exklusivt eller)
    • not (enkel logiskt inte)

    Jämförelseoperatörer:

    • = (lika med)
    • < och > (mindre än, större än)
    • <= och >= (mindre än eller lika med, större än eller lika med)
    • <> (inte lika med)

    För tilldelning används := (kolon och lika med).

    Typomvandling

    För konvertering från tal till sträng används Str, CurrToStr, Format, IntToStr och IntToHex. Notera att Str tar en sträng som utparameter med resultatet istället för att ge resultatet i retur.

    var
      i : Integer;
      s : String;
      c : Currency;
    begin
      //Konvertera Integer till String med s som utparameter:
      i := 10;
      Str(i, s);
      //Konvertera Currentsy till String:
      c := 100;
      s := CurrToStr(c);

    För att konvertera från sträng till tal används StrToInt, StrToIntDef (feltolerant konvertering) och StrToFloat.

    var
      f : Extended;
      format : TFormatSettings;
    begin
      //Konvertera String till Integer (undantagsfel vid misslyckad konvertering):
      writeln(StrToInt('500'));
      //Konvertera String till Integer (0 vid misslyckad konvertering):
      writeln(StrToIntDef('1000', 0));
      //Konvertera String till Extended (undantagsfel vid misslyckad konvertering):
      format := TFormatSettings.create;
      format.DecimalSeparator := ',';
      writeln(StrToFloat('100,5', format));

    Typer

    Typer deklareras i interface-sektionen i en enhet. De inleds med type följt av namn och supertyp, och avslutas med end; (notera avslutande semikolon). Detta exempel visar två typer:

    type
      MinTyp = class(TObject)
      end;
    
    type
      MinAndraTyp = class(TObject)
      end;

    Supertypen TObject behöver inte anges, eftersom den antas gälla. Deklarationerna delas upp efter synlighet. Följande exempel utrustar typen Employee med två privata medlemsvariabler.

    type
      Employee = class
    private
      //Privata variabler
      _firstName : string;
      _lastName : string;
      end;

    Förutom synlighetsnivån private finns nivåerna protected, public och published. Nivån private anger att medlemmen är lokal för objektet, protected innebär att typer som ärver från typen har tillgång till medlemmen, public innebär att alla andra objekt i samma projekt har tillgång till medlemmen, och published innebär att medlemmen dessutom är tillgänglig för object inspectorn i utvecklingsmiljön.

    Funktioner

    Funktioner deklareras med nyckelordet function, följt av funktionens namn, parameterlista och returtyp enligt följande:

    function Namn(parameter: typ; parameter: typ) : returtyp;

    Om funktionen saknar retur, används istället nyckelordet procedure.

    procedure Namn(parameter: typ; parameter: typ)

    Parenteserna efter namnet är inte nödvändiga om funktionen eller proceduren inte tar några parametrar. Detta exempel visar funktionen getName:

    type
      Employee = class
      private
        //Privata variabler
        _firstName : string;
        _lastName : string;
        function getName : string;
      end;

    Implementationerna sker i sektionen implementation. Man använder typnamn.funktionsnamn för att ange vilken funktion som implementeras. Implementationen avgränsas med begin och end; (semikolon). Det är inte obligatoriskt att skicka tillbaka ett värde, trots att man deklarerat en returtyp. För att skicka tillbaka ett värde görs en tilldelning till result.

    function Employee.getName: string;
    begin
      result :=  (_firstName + ' ' + _lastName);
    end;

    Däremot är det inte tillåtet att läsa oinitierade strängar. Ett sätt att utrusta _firstName och _lastName med värden är att implementera en konstruktor. Dessa ska enligt standarden heta create, och kan överlagras för att erbjuda flera sätt att initiera objekten.

    public
      constructor create; overload;
      constructor create(firstName: string; lastName: string); overload;

    Konstruktorn måste anropas uttryckligen.

    e := Employee.create;

    Properties

    Properties är inte lika mångsidiga som i många andra språk. I Delphi anger man endast vilken medlemsvariabel som påverkas vid tilldelning till en property, och vilken som påverkas av avläsning. Syntaxen för en property är:

    property namn : typ read uttryck write uttryck;

    För att skapa en read only property utelämnas write uttryck, och för att skapa en read only property utelämnas write uttryck. Exempel:

    type
      Point = class
      private
        _x : integer;
        _y : integer;
      public
        property x : integer read _x write _x;
        property y : integer read _y write _y;
      end;

    Ett exempel

    Följande exempel visar deklarationen av en klass (sektionen interface) som representerar en person. Klassen innehåller privata variabler, en privat funktion, två konstruktorer, två read/write properties samt en read only property som använder en funktion som uttryck.

    type
      Employee = class
      private
        //Privata variabler
        _firstName : string;
        _lastName : string;
        function getName : string;
      public
        constructor create; overload;
        constructor create(firstName: string; lastName: string); overload;
        property firstName : string read _firstName write _firstName;
        property lastName : string read _lastName write _lastName;
        property name : string read getName;
      end;

    Så här ser implementationerna ut (sektionen implementation):

    constructor Employee.create;
    begin
      _firstName := '';
      _lastName := '';
    end;
    
    constructor Employee.create(firstName, lastName: string);
    begin
      _firstName := firstName;
      _lastName := lastName;
    end;
    
    function Employee.getName: string;
      begin
      result :=  (_firstName + ' ' + _lastName);
    end;

    Här följer ett exempel:

    var
      e : Employee;
    begin
      e := Employee.create('Sven', 'Svensson');
      writeln(e.name);