Skip to content

Blog su Lazarus e il Pascal

Scrivi una volta e compila dove vuoi…

TNullable

Posted on 27 Dicembre 20215 Gennaio 2022 By nomorelogic Nessun commento su TNullable

Obiettivo

Definizione di un tipo dati che possa rappresentare anche i valori null, caratteristici nei database RDBMS, nei casi di colonne non inizializzate con alcun valore.

Generalità

Lavorando con i database, spesso ci si trova a dover gestire i valori NULL che troviamo in alcune colonne di una determinata tabella.

Le interpretazioni che si potrebbero dare a questo tipo di valore potrebbero essere diverse ma, quella che più rende l’idea, è che NULL rappresenta un “valore non definito” e, ciò che ci interessa a questo punto, è che dobbiamo gestirlo nei nostri programmi.

Spesso si tende a convertire il NULL con quello che potrebbe essere il valore nullo corrispondente a seconda del tipo: ad esempio stringa vuota per i campi carattere e 0 (zero) o -1 per i campi numerici.
Questo approccio non è proprio corretto e può portare a diversi problemi: uno su tutti, se abbiamo una stringa nulla da salvare nel database non sappiamo se salvarla come NULL o, appunto, come stringa nulla.

Si potrebbe usare il tipo Variant ma, nei sorgenti del Free Pascal, si trova definito il tipo TNullable. Si tratta di un advanced record – che ha subito diverse modifiche nel tempo – proveniente dal mondo Delphi e usato per testare la compatibilità del compilatore Free Pascal con Delphi.

Tralasciando storia e alternative, è comunque un buon approccio a questo tipo di problema nonché un modo di mettere in pratica diverse tecniche in una unica soluzione:

  • sintassi avanzata dei record;
  • tipi generici e specializzazioni.

Il sorgente di TNullable (attualmente lo si trova solo nei sorgenti della trunk, ma dovrebbe essere nella stable a partire dalla prossima versione di Lazarus 2.2.0) lo troviamo in:

lazarus/fpcsrc/packages/rtl-objpas/src/inc/nullable.pp

Mentre il progetto di test lo troviamo in:

lazarus/fpcsrc/tests/test/units/nullable/tnull.pp

unit

Andiamo ora ad analizzare come è stato realizzato:

unit nullable;

{$mode objfpc}
{$modeswitch advancedrecords}
  • è attiva la modalità objfpc (modalità di default per i sorgenti Lazarus);
  • è stato attivato lo switch advancedrecords, che permette l’utilizzo di funzioni e procedure anche nei tipi record;

interface

Nella sezione interface, troviamo la definizione del record come generic.

Il generic è stato utilizzato per evitare di dover definire un tipo nullable per ogni tipo nativo. Brevemente – e solo per analogia – possiamo dire che il generic sta ad una classe-con-metodi-astratti come la specializzazione (di un generic) sta a quella classe che poi implementerà i metodi astratti.

Più avanti, quando vedremo le specializzazioni sarà più chiaro.

Type

  { TNullable }

  generic TNullable<T> = record
    ...
  Public
    procedure Clear ...
    property HasValue: Boolean ...
    property IsNull: Boolean ...
    property Value: T ...
    property ValueOrDefault: T ...
    class function Empty: TMyType; static;

    class operator Initialize ...
    class operator Explicit(aValue: T): TMyType;
    class operator Explicit(aValue: TMyType): T;
    class operator := (aValue: T): TMyType;
    class operator := (aValue: TMyType): T;
   end;

Nella sezione public troviamo le definizioni ci interessano per l’utilizzo di questo tipo di record.

procedure Clear;

Invocando Clear, la variabile verrà impostata come valore nullo.

property HasValue: Boolean read FHasValue write SetHasValue;

Se la variabile è nulla, HasValue restituirà False; se invece è valorizzata, HasValue restituirà True.

property IsNull: Boolean read GetIsNull;

Se la variabile è nulla, IsNull restituirà True; se invece è valorizzata restituirà False.

property Value: T read GetValue write SetValue;

La proprietà Value verrà utilizzata per leggere il valore.

property ValueOrDefault: T read GetValueOrDefault;

La proprietà ValueOrDefault è utile quando la variabile non è valorizzata ma si vuole il valore di default del tipo specializzato.

class function Empty: TMyType; static;

Funzione del tipo (anche se la dichiarazione è class function) che restituisce un valore vuoto.

class operator Initialize(var aSelf : TNullable);

E’ il costruttore, serve ad in inizializzare la variabile.
In questo caso il codice (lo trovate nella sezione implementation) è:
aSelf.FHasValue:=False;

class operator Explicit(aValue: T): TmyType;

Definizione class operator nei casi di cast esplicito verso un tipo nullable.

Esempio:

Var
  A : specialize TNullable<String>;
  B : String;
begin
  B:='ciao';
  A:=specialize Tnullable<String>(B); // <- cast esplicito
class operator Explicit(aValue: TMyType): T;

Definizione class operator nei casi di cast esplicito verso un tipo.
Esempio:

Var
  A : specialize TNullable<String>;
  B : String;
begin
  a.Value:='ciao';
  B:=String(A);  // <- cast esplicito
class operator := (aValue: T): TmyType;

Definizione class operator nei casi di assegnazione di un valore ad un tipo nullable specializzato.

Esempio:

Var
  A : specialize TNullable<String>;
  B : String;
begin
  Result:='';
  A.Value:=Val1;
  B:=A; // <- assegnazione da nullable specializzato a tipo

Specializzazione

La specializzazione si rende necessaria per poter utilizzare un generic mappandolo ad un tipo esistente.

Ad esempio, se volessimo un tipo nullable sia per gli interi che per le stringhe, potremmo dichiarare:

Type
  TNullableString = specialize TNullable<String>;
  TNullableInteger = specialize Tnullable<Integer>;

In alternativa, possiamo dichiarare una variabile nullable specializzata, direttamente nella sezione var senza dover definire un nuovo tipo specializzato.

Ad Esempio:

Var
  A : specialize TNullable<String>;

Conclusioni

La definizione di TNullable descritta qui è un concentrato di tecniche che si possono utilizzare con il pascal ad oggetti.
Non è solo una definizione accademica ma è anche e soprattutto una soluzione pratica ad un problema esistente con un approccio moderno.

Spero questo articolo sia stato utile e che possa dare spunto per fare sempre meglio.

Strutture dati Tags:advanced record, TNullable

Navigazione articoli

Previous Post: Installare Lazarus su Windows
Next Post: Installare Lazarus e FPC con FPCupDeluxe

Lascia un commento Annulla risposta

Devi essere connesso per inviare un commento.

Archivi

  • Maggio 2025
  • Aprile 2025
  • Ottobre 2024
  • Maggio 2024
  • Marzo 2024
  • Dicembre 2023
  • Novembre 2023
  • Settembre 2023
  • Giugno 2023
  • Maggio 2023
  • Marzo 2023
  • Maggio 2022
  • Aprile 2022
  • Febbraio 2022
  • Gennaio 2022
  • Dicembre 2021

Categorie

  • Android
  • Funzioni avanzate
  • Installazione alternativa di Lazarus e Free Pascal Compiler (FPC)
  • Installazioni
  • Lazarus ed il Web
  • Lazarus, l'IDE ed i suoi strumenti
  • Sintassi e concetti base
  • Strutture dati
  • Tutorial
Vai al forum Italiano

Articoli recenti

  • Il framework Brook – tutorial 2 – un server CRUD
  • Il framework Brook – tutorial 1 – primi passi
  • Installazione di Lazarus in Linux – Risoluzione problemi
  • Autenticazione 2FA
  • Installazione di Free Pascal e Lazarus su Manjaro Linux

Commenti recenti

  1. DragoRosso su Installazione di Lazarus e Free Pascal – su Linux – tramite package manager a linea di comando

Copyright © 2025 Blog su Lazarus e il Pascal.

Powered by PressBook WordPress theme

Utilizziamo i cookie per essere sicuri che tu possa avere la migliore esperienza sul nostro sito. Se continui ad utilizzare questo sito noi assumiamo che tu ne sia felice.Ok