Delphi Managed Records - Making your code smart

With Delphi 10.4, Managed Records language feature was introduced. As some might remember, it was originally intended for Delphi 10.3, but Embarcadero wisely decided to postpone the feature, to ensure a solid implementation - and now we have it!

It is important to mention, this is a language feature available with Delphi 10.4 and newer.

Managed Records opens up many new possibilities to write smarter and more effective code without syntactic sugar and bloat which other popular languages suffer from. This is one of the many reasons I keep using Delphi - the language is simple, yet powerful and allows you to eloquently write software.
Writing software is after all an art, just like writing a novel.

Anyways, back to the actual topic - Managed Records.

In this article, I want to share a little clever tidbit of how you can utilize the automatic initialization and finalization calls of Managed Records. 
INI files is still one of the easiest ways to have a key-value settings storage across platforms, and usually it takes some legwork to create the object, read or write your settings and ensuring the object is destroyed again.
What if you could access the INI Read or Write features right away and don't worry about keeping track of the object life-cycle?
- Managed Records to the rescue!

In a recent feedback tool I've been working on for another client project, I had the need to save and load a few settings throughout the application.
I figured I didn't want to do the classic create object, do my operation in a try-finally block and close down the object again and thought that Managed Records automatic initialization and finalization should be able to do the trick.
The following snippet illustrates how this can be done:

unit MRTest.AppSettings;

interface

uses
  System.SysUtils,
  System.Classes,
  System.IOUtils,
  System.IniFiles;

type
  TAppSettings = record
  strict private const
    cIniFileName: string = 'TestSettings.ini';
  strict private
    FAppConf: TMemIniFile;
    class function TAppSettings.InternalGetFullIniFile: string; static;
  public
    class operator Initialize(out ADest: TAppSettings);
    class operator Finalize(var ADest: TAppSettings);
    property AppConf: TMemIniFile read FAppConf write FAppConf;
  end;

implementation

{ TAppSettings }

class operator TAppSettings.Initialize(out ADest: TAppSettings);
begin
  ADest.FAppConf := TMemIniFile.Create(ADest.InternalGetFullIniFile);
  ADest.FAppConf.AutoSave := True;
end;

class operator TAppSettings.Finalize(var ADest: TAppSettings);
begin
  ADest.FAppConf.Free;
end;

class function TAppSettings.InternalGetFullIniFile: string;
begin
  Result := TPath.Combine(TPath.GetDocumentsPath, cIniFileName);
end;

end.

Now you can just include the unit in your project where needed and automatically allocate a variable of the type TAppSettings (Or whatever name your record type has).
The example below has been reduced for the simplicity to give you a gist of how it can be approached.

uses
  MRTest.AppSettings;


procedure LoadSettings;
begin
  var LApp: TAppSettings;
  CheckBox1.Checked := LApp.AppConf.ReadBool('Settings', 'FirstStart', True);
end;

procedure SaveSettings;
begin
  var LApp: TAppSettings;
  LApp.AppConf.WriteBool('Settings','FirstStart', CheckBox1.Checked);
end;

As you see, everything is encapsulated in the record type and the managed nature automatically takes care of creating the INI object with the correct settings when it goes into scope, and unloads it again once it goes out of scope.

I hope you found this information useful.