Are you Clear on how Clear() works in AL?

Sometimes, it’s the small and clearly obvious commands in a programming language that can come back and bite you. Here’s the story of how Clear() did just that. Check out the video:

https://youtu.be/bacaGbsTeaw

In this video, Erik dives into the nuances of the Clear() function in AL for Business Central — a function that seems straightforward until you encounter single instance codeunits, record references, and init values. What starts as a simple demonstration quickly reveals surprising behavior that can bite you in production code.

The Basic Setup: Clear on a Codeunit

Erik starts with a simple example. Imagine you have a codeunit with a text variable, along with Set and Get procedures — essentially acting like a simple object with a property:

codeunit 50100 Test
{
    var
        val: Text;

    procedure Set(val_: Text)
    begin
        val := val_;
    end;

    procedure Get(): Text
    begin
        exit(val);
    end;
}

Then, from a calling context, you create an instance, set a value, clear the codeunit, and try to retrieve the value:

var
    t: Codeunit Test;
begin
    t.Set('Hello');
    Clear(t);
    Message(t.Get());
end;

The result? A blank message. That’s exactly what you’d expect — nothing surprising yet.

The Plot Twist: Single Instance Codeunits

Now here’s where it gets interesting. Erik adds the SingleInstance property to the codeunit:

codeunit 50100 Test
{
    SingleInstance = true;
    // ...
}

Running the exact same calling code — set “Hello”, clear the codeunit, get the value — now returns “Hello” from a supposedly cleared codeunit.

So what does Clear() actually do?

What the Documentation Says

Erik walks through the official documentation for Clear():

  • It clears the value of a single variable.
  • For records, it clears all filters, resets the key to the primary key, and resets field values.
  • For composite data types like records and arrays, all elements are cleared.

But here’s the critical line from the docs:

“If you use Clear on a codeunit, only the reference to the codeunit is deleted and not the codeunit itself, as with automation objects. This means that the content of the codeunit stays intact.”

Understanding the Behavior: References vs. Instances

In AL, you don’t explicitly instantiate codeunits the way you would in C# (with new). An instance is automatically created when you first interact with the codeunit variable. Here’s what’s happening under the hood:

  1. Without SingleInstance: When you call t.Set('Hello'), you get instance #1. When you call Clear(t), the reference to instance #1 is deleted. The next time you call t.Get(), a brand new instance #2 is created — which is blank, because instance #1 is gone (eventually garbage collected).
  2. With SingleInstance: Every reference to the codeunit points to the same entity in memory for the entire session. So when you clear the reference and then touch the codeunit again, you get a new reference — but it still points to the same single instance. The data is still there.

How to Actually Clear a Single Instance Codeunit

If you need to reset the state of a single instance codeunit, you can’t rely on Clear() from outside. Instead, create an internal procedure that clears the state:

codeunit 50100 Test
{
    SingleInstance = true;

    var
        val: Text;

    procedure Set(val_: Text)
    begin
        val := val_;
    end;

    procedure Get(): Text
    begin
        exit(val);
    end;

    procedure ClearState()
    begin
        ClearAll();
    end;
}

Then call t.ClearState() instead of Clear(t). The difference between Clear() and ClearAll() matters here — ClearAll() executed from within the codeunit clears all the variables in that context, effectively resetting the single instance’s internal state.

Bonus: Clear and Init Values on Records

Erik also covers an important detail about how Clear() interacts with records that have init values defined. The source code demonstrates this perfectly with a test table:

table 50118 "Test Table"
{
    fields
    {
        field(1; Primary; Code[20])
        {
            InitValue = 'ABC';
        }
        field(3; Texti; Text[100])
        {
            InitValue = 'Hello';
        }
        field(4; ANumber; Decimal)
        {
            InitValue = 123.45;
        }
        field(10; SomeDay; Date)
        {
            InitValue = 20220101D;
        }
    }
    keys
    {
        key(PK; Primary)
        { }
    }
}

And the page extension that tests the behavior:

pageextension 50100 CustomerListExt extends "Customer List"
{
    trigger OnOpenPage();
    var
        x: Record "Test Table";
        y: Record "Test Table";
    begin
        clear(x);
        y.Texti := '';
        y.init();
        Message('%1 vs %2', format(x), format(y));
    end;
}

The key takeaway: Clear() on a record resets fields to their init values, not to blank/zero. So if you have a field with InitValue = 'ABC', clearing the record will set that field to 'ABC', not to an empty string. Similarly, Init() sets fields to their init values. However, if you clear a standalone integer variable (not a field), it goes to zero — init values are a table field concept.

Bonus Bonus: Clear on RecordRef

Erik raises one more edge case: what happens when you call Clear() on a RecordRef?

var
    ref: RecordRef;
begin
    ref.Open(Database::Customer);
    ref.FindFirst();
    Clear(ref);
end;

The answer: Clear() clears the reference itself, not the underlying record it points to. The documentation doesn’t even mention RecordRef in the context of Clear(). Erik notes that there is no built-in way to clear the record a RecordRef points to (resetting it to init values), which is something he’s had to implement manually — and it’s tricky because accessing init values through a RecordRef isn’t straightforward.

Practical Advice: Single Instance Codeunits

Erik wraps up with some practical context on single instance codeunits:

  • SingleInstance means per session — not per environment, not shared across all users. It’s scoped to the current user’s session.
  • They’re a convenient way to get persistent memory between events — particularly useful when you need to pass data between one event subscriber and another within the same session.
  • When you switch a codeunit to single instance, you need to rethink your clearing strategy. Any Clear() calls on that codeunit from outside won’t actually reset its state.
  • Be mindful of memory — since the instance persists for the session, data can accumulate if you’re not explicitly clearing it at the right time.

Summary

The Clear() function in AL is more nuanced than it appears at first glance. For regular codeunits, it deletes the reference and a new blank instance is created on next use. For single instance codeunits, clearing the reference is essentially a no-op since the next access reconnects to the same persistent instance — you need to clear state from within the codeunit using ClearAll(). For records, Clear() resets to init values rather than blank. And for RecordRef, it clears only the reference, not the record data. Understanding these distinctions can save you from subtle bugs that are difficult to track down.