Introduction

Generics and collections are powerful features in Delphi/Object Pascal that allow you to create flexible and reusable code. Generics enable you to define classes, procedures, and functions with a placeholder for the data type, which can be specified when the generic is instantiated. Collections, on the other hand, are data structures that store and manage groups of objects.

In this section, we will cover:

  • Understanding Generics
  • Using Generic Classes and Methods
  • Common Collection Types
  • Practical Examples
  • Exercises

Understanding Generics

Generics allow you to write code that can operate on different data types without being rewritten for each type. This is achieved by defining a generic type parameter.

Key Concepts

  • Type Parameter: A placeholder for a data type that is specified when the generic is instantiated.
  • Generic Class: A class that uses type parameters.
  • Generic Method: A method that uses type parameters.

Syntax

Here is the basic syntax for defining a generic class:

type
  TGenericClass<T> = class
  private
    FValue: T;
  public
    constructor Create(AValue: T);
    function GetValue: T;
    procedure SetValue(AValue: T);
  end;

In this example, T is a type parameter that will be replaced with a specific type when the class is instantiated.

Using Generic Classes and Methods

Example: Generic Class

Let's create a simple generic class to store and retrieve a value:

type
  TBox<T> = class
  private
    FValue: T;
  public
    constructor Create(AValue: T);
    function GetValue: T;
    procedure SetValue(AValue: T);
  end;

constructor TBox<T>.Create(AValue: T);
begin
  FValue := AValue;
end;

function TBox<T>.GetValue: T;
begin
  Result := FValue;
end;

procedure TBox<T>.SetValue(AValue: T);
begin
  FValue := AValue;
end;

Instantiating a Generic Class

You can instantiate the generic class with different types:

var
  IntBox: TBox<Integer>;
  StrBox: TBox<String>;
begin
  IntBox := TBox<Integer>.Create(123);
  StrBox := TBox<String>.Create('Hello, Generics');
  
  Writeln(IntBox.GetValue); // Output: 123
  Writeln(StrBox.GetValue); // Output: Hello, Generics
end;

Example: Generic Method

You can also define generic methods within a class:

type
  TUtils = class
  public
    class function Swap<T>(var A, B: T): Boolean;
  end;

class function TUtils.Swap<T>(var A, B: T): Boolean;
var
  Temp: T;
begin
  Temp := A;
  A := B;
  B := Temp;
  Result := True;
end;

Using a Generic Method

var
  X, Y: Integer;
begin
  X := 10;
  Y := 20;
  TUtils.Swap<Integer>(X, Y);
  Writeln(X); // Output: 20
  Writeln(Y); // Output: 10
end;

Common Collection Types

Delphi provides several generic collection types in the Generics.Collections unit:

  • TList<T>: A dynamic array of items.
  • TDictionary<TKey, TValue>: A collection of key-value pairs.
  • TQueue<T>: A first-in, first-out (FIFO) collection.
  • TStack<T>: A last-in, first-out (LIFO) collection.

Example: TList

uses
  System.Generics.Collections;

var
  IntList: TList<Integer>;
begin
  IntList := TList<Integer>.Create;
  try
    IntList.Add(1);
    IntList.Add(2);
    IntList.Add(3);
    
    Writeln(IntList[0]); // Output: 1
    Writeln(IntList[1]); // Output: 2
    Writeln(IntList[2]); // Output: 3
  finally
    IntList.Free;
  end;
end;

Example: TDictionary<TKey, TValue>

uses
  System.Generics.Collections;

var
  Dict: TDictionary<String, Integer>;
begin
  Dict := TDictionary<String, Integer>.Create;
  try
    Dict.Add('One', 1);
    Dict.Add('Two', 2);
    Dict.Add('Three', 3);
    
    Writeln(Dict['One']); // Output: 1
    Writeln(Dict['Two']); // Output: 2
    Writeln(Dict['Three']); // Output: 3
  finally
    Dict.Free;
  end;
end;

Practical Examples

Example: Using TQueue

uses
  System.Generics.Collections;

var
  Queue: TQueue<String>;
begin
  Queue := TQueue<String>.Create;
  try
    Queue.Enqueue('First');
    Queue.Enqueue('Second');
    Queue.Enqueue('Third');
    
    Writeln(Queue.Dequeue); // Output: First
    Writeln(Queue.Dequeue); // Output: Second
    Writeln(Queue.Dequeue); // Output: Third
  finally
    Queue.Free;
  end;
end;

Example: Using TStack

uses
  System.Generics.Collections;

var
  Stack: TStack<String>;
begin
  Stack := TStack<String>.Create;
  try
    Stack.Push('First');
    Stack.Push('Second');
    Stack.Push('Third');
    
    Writeln(Stack.Pop); // Output: Third
    Writeln(Stack.Pop); // Output: Second
    Writeln(Stack.Pop); // Output: First
  finally
    Stack.Free;
  end;
end;

Exercises

Exercise 1: Create a Generic Pair Class

Create a generic class TPair<T1, T2> that stores a pair of values. Implement methods to get and set the values.

Solution:

type
  TPair<T1, T2> = class
  private
    FFirst: T1;
    FSecond: T2;
  public
    constructor Create(AFirst: T1; ASecond: T2);
    function GetFirst: T1;
    function GetSecond: T2;
    procedure SetFirst(AFirst: T1);
    procedure SetSecond(ASecond: T2);
  end;

constructor TPair<T1, T2>.Create(AFirst: T1; ASecond: T2);
begin
  FFirst := AFirst;
  FSecond := ASecond;
end;

function TPair<T1, T2>.GetFirst: T1;
begin
  Result := FFirst;
end;

function TPair<T1, T2>.GetSecond: T2;
begin
  Result := FSecond;
end;

procedure TPair<T1, T2>.SetFirst(AFirst: T1);
begin
  FFirst := AFirst;
end;

procedure TPair<T1, T2>.SetSecond(ASecond: T2);
begin
  FSecond := ASecond;
end;

Exercise 2: Use TDictionary to Count Word Frequencies

Write a program that reads a list of words and uses TDictionary<String, Integer> to count the frequency of each word.

Solution:

uses
  System.Generics.Collections, System.SysUtils;

var
  Words: TArray<String>;
  WordCount: TDictionary<String, Integer>;
  Word: String;
begin
  Words := ['apple', 'banana', 'apple', 'orange', 'banana', 'apple'];
  WordCount := TDictionary<String, Integer>.Create;
  try
    for Word in Words do
    begin
      if WordCount.ContainsKey(Word) then
        WordCount[Word] := WordCount[Word] + 1
      else
        WordCount.Add(Word, 1);
    end;
    
    for Word in WordCount.Keys do
      Writeln(Word, ': ', WordCount[Word]);
  finally
    WordCount.Free;
  end;
end;

Conclusion

In this section, we explored the concept of generics and collections in Delphi/Object Pascal. We learned how to define and use generic classes and methods, and we examined some common collection types provided by Delphi. By understanding and utilizing these features, you can write more flexible, reusable, and efficient code.

Next, we will delve into multithreading and parallel programming, where we will learn how to execute multiple tasks concurrently to improve the performance of our applications.

Delphi/Object Pascal Programming Course

Module 1: Introduction to Delphi/Object Pascal

Module 2: Control Structures and Procedures

Module 3: Working with Data

Module 4: Object-Oriented Programming

Module 5: Advanced Delphi Features

Module 6: GUI Development with VCL and FMX

Module 7: Web and Mobile Development

Module 8: Best Practices and Design Patterns

Module 9: Final Project

© Copyright 2024. All rights reserved