Fabrizio
Fabrizio

Reputation: 8043

Why does TJSONObject.AddPair results Self?

I've noticed that TJSONObject.AddPair functions results Self instead of the newly created object:

For example, in System.JSON unit I see the following code:

function TJSONObject.AddPair(const Str: string; const Val: string): TJSONObject;
begin
  if (not Str.IsEmpty) and (not Val.IsEmpty) then
    AddPair(TJSONPair.Create(Str, Val));
  Result := Self;
end;

I was expecting something like that:

function TJSONObject.AddPair(const Str: string; const Val: string): TJSONObject;
begin
  if (not Str.IsEmpty) and (not Val.IsEmpty) then
    Result := AddPair(TJSONPair.Create(Str, Val));
  else 
    Result := nil;
end;

I find this very unusual, is it a Delphi XE7 bug or is there any technical/practical reason why they did that?

Upvotes: 2

Views: 731

Answers (1)

Dalija Prasnikar
Dalija Prasnikar

Reputation: 28516

Returning Self is common coding pattern called fluent interface.

It allows you to continue with calls to the same object, creating chain of methods without the need to reference object variable for every call. That makes code more readable, on the other hand it is harder to debug.

var
  Obj: TJSONObject;
begin
  Obj := TJSONObject.Create
    .AddPair('first', 'abc')
    .AddPair('second', '123')
    .AddPair('third', 'aaa');
 ...
end;

would be equivalent of

var
  Obj: TJSONObject;
begin
  Obj := TJSONObject.Create;
  Obj.AddPair('first', 'abc');
  Obj.AddPair('second', '123');
  Obj.AddPair('third', 'aaa');
 ...
end;

And the generated JSON object will look like:

{
  "first": "abc",
  "second": "123",
  "third": "aaa"
}

That kind of coding style is more prevalent in languages with automatic memory management, because you don't need to introduce intermediate variables.

For instance, if you need JSON string you would use following construct:

var
  s: string;
begin
  s := TJSONObject.Create
    .AddPair('first', 'abc')
    .AddPair('second', '123')
    .AddPair('third', 'aaa')
    .Format(2);
 ...
end;

The problem with the above code in Delphi is that it creates memory leak as you don't have ability to release intermediate object. Because of that, it is more common to use fluent interface pattern in combination with reference counted classes where automatic memory management will handle releasing of any intermediary object instances.

Upvotes: 6

Related Questions