Jump to content
Mike Torrettinni

Disadvantage of using defined type of TArray?

Recommended Posts

I'm not sure the verbiage of title is correct, but here is the question:

 

when I have TItem and TItems defined as:

type
  TItem = record
    ...
  end;

  TItems: TArray<TItem>;
  

Is there any obvious disadvantage of using TItems instead of using everywhere TArray<TItem> for variables, function results, parameters... ?

 

I don't know how to phrase the question to ask google about this, in case this is widely discussed topic already.

 

Share this post


Link to post
Posted (edited)

I think you meant to write:

TItems = TArray<TItem>;

Apart from not having to type the angle brackets anymore this is exactly the same type as TItems is just an alias for TArray<TItem>

Edited by Stefan Glienke
  • Like 1
  • Thanks 1

Share this post


Link to post
32 minutes ago, Stefan Glienke said:

I think you meant to write:


TItems = TArray<TItem>;

Apart from not having to type the angle brackets anymore this is exactly the same type as TItems is just an alias for TArray<TItem>

Does using a defined type play any part in reducing the bloat of generics?

Share this post


Link to post
39 minutes ago, Stefan Glienke said:

I think you meant to write:


TItems = TArray<TItem>;

Apart from not having to type the angle brackets anymore this is exactly the same type as TItems is just an alias for TArray<TItem>

Great, thanks for confirming. I knew such alias can be defined, but never really thought of defining and using it for my own arrays. Now I will! 🙂

 

Thanks, when I search for alias I get some results.

Share this post


Link to post

Yes, there is a disadvantage of using TArray<TItem> instead of TItems. In the same unit where class is declared, whenever compiler found explicit generic type(TArray<TItem>), compiler must do extra work : matching arguments, checking for constraints, ...  In a large unit that uses generics massively, this may add a little overhead. TItems on the other side, works as a cache (compiler does not need to check constraints for example).
Using TArray<TItem> from another unit adds a noticeable overhead as the compiler must generate the type in-situ for that unit. In fact do the following test yourself: 

unit Unit1;

interface

uses
  System.SysUtils,
  System.Generics.Collections,
  System.Classes;

type
  TObject<T> = class
    a: T;
    b: T;
    procedure foo(a, b: T);
  end;

  TListOfInteger = TList<TObject<Integer>>;
implementation

{ TObject<T> }

procedure TObject<T>.foo(a, b: T);
begin

end;

end.

// ---------------------------------------
unit Unit2;

interface

uses

  System.SysUtils,
  System.Generics.Collections,
  System.Classes, Unit1;

type
  TListOfInteger2 = TList<TObject<Integer>>;

implementation

end.

//-----------------------------------------
unit Unit3;

interface

uses

  System.SysUtils,
  System.Generics.Collections,
  System.Classes, Unit1;

type
  TListOfInteger3 = TListOfInteger; // alias

implementation

end.

Now, check the size of Unit1.dcu, Unit2.dcu, Unit3.dcu.
Final thing, TItems is more friendly for typing and reading !

  • Like 2

Share this post


Link to post
13 minutes ago, Mahdi Safsafi said:

Now, check the size of Unit1.dcu, Unit2.dcu, Unit3.dcu.
Final thing, TItems is more friendly for typing and reading !

 

Thanks for simple example, I see the difference. I use TArray<> a lot, will see if I can get used to defining these aliases.

 

Share this post


Link to post
Posted (edited)
12 minutes ago, David Heffernan said:

You are just polluting the namespace for no benefit. Use TArray<T>. 

 

Isn't TArray<T> only for generic usage? I use it when I define generic method, and is used like TArray,Method<T>... Can it be used for specific usage, like:

 

type
  TItem = record
    ID: integer;
    Text: string;
    ChildCount: integer;
  end;

  fData = TArry<TItem>;
    
function GetText(aIdx: integer): string;
begin
  Result := fData[aIdx].Text;
end;
    

Not sure how TArray<T> could be used in this example?

Edited by Mike Torrettinni

Share this post


Link to post
29 minutes ago, Mike Torrettinni said:

Isn't TArray<T> only for generic usage? I use it when I define generic method, and is used like TArray,Method<T>.

Those are two completely different things. One is a generic dynamic array type, the other is a generic method. 

Share this post


Link to post
8 hours ago, David Heffernan said:

You are just polluting the namespace for no benefit. Use TArray<T>. 

 

As a benefit I see better readability and flexibility.

In case we decide to change TArray<T> to something like a TList<T> later, we only have to change that in one place. (Of course the code itself might have to change, too. But that has to be done anyway.)

 

I often start with an alias like TItems = TList<T> and later extend that to TItems = class(TList<T>) with some additional or overridden functionality. Sometimes it is just to hide the necessary constructor parameters inside a simple TItems.Create.

 

If the implementation is indeed relevant for understanding the code, I use type names like TItemArray and TItemList, even if those are also just aliases to TArray<TItem> and TList<TItem>.

 

For me, these benefits are of much more value than keeping the namespace small. The latter may result in shorter compile times, which are already pretty short. Runtime performance and code maintainability gain near to nothing from it.

  • Like 4

Share this post


Link to post

I can't really see any benefit here. I mean you might think that TItemArray is somehow better than TArray<TItem> but they seem pretty interchangeable to me in terms of readability. And the reader has to trust that convention was followed with TItemArray. 

  • Like 1

Share this post


Link to post
Posted (edited)
12 hours ago, Bill Meyer said:

Does using a defined type play any part in reducing the bloat of generics?

Not for TArray<T> but possibly for types with actual executable code - see my reply to Mahdi.

 

12 hours ago, Mahdi Safsafi said:

Now, check the size of Unit1.dcu, Unit2.dcu, Unit3.dcu.
Final thing, TItems is more friendly for typing and reading !

The question was about TArray<T> where it does not matter at all rather than a few unnoticable microseconds at compile time.

You are right however when talking about types that have executable code (and possibly a significant amount of typeinfo) as the compiler always emits all code of a generic type into each and every dcu that is using it as in your example with Unit1 and Unit2.

However it does not need to emit into Unit3.dcu because that one is just referencing the type that already fully resides in Unit2.

Edited by Stefan Glienke
  • Like 1

Share this post


Link to post
Quote

The question was about TArray<T> where it does not matter at all rather than a few unnoticable microseconds at compile time.

Just for clarification, I used two different word :  little overhead & noticeable overhead to distinguish between two different usage of TArray<T>. 

 

Quote

You are right however when talking about types that have executable code (and possibly a significant amount of typeinfo) as the compiler always emits all code of a generic type into each and every dcu that is using it as in your example with Unit1 and Unit2.

However it does not need to emit into Unit3.dcu because that one is just referencing the type that already fully resides in Unit2.

You definitely understood my example :classic_smile: In fact, for Unit3, compiler only emitted interface for the alias-type without implementation (without machine code generation). For Unit1 and Unit2 it emitted the interface and the implementation (generated code). 

Share this post


Link to post

I can remember the post on G+ by Stefan about the bloated .dcu's and the affected linking time, but this is the first time I see a "workaround".

Share this post


Link to post
38 minutes ago, Mahdi Safsafi said:

Just for clarification, I used two different word :  little overhead & noticeable overhead to distinguish between two different usage of TArray<T>. 

I can't see a situation where TArray<T> can have noticable overhead as it does not have enough stuff to be generated - what you demonstrated was using a generic class that has executable code and RTTI.

Share this post


Link to post
7 hours ago, David Heffernan said:

I can't really see any benefit here. I mean you might think that TItemArray is somehow better than TArray<TItem> but they seem pretty interchangeable to me in terms of readability. And the reader has to trust that convention was followed with TItemArray. 

I can see also benefit in maintaining code.

Imaging you change

TItemArray = TArray<TItem>;

to

TItemArray = TArray<TItemEx>;

In the TItemArray version you touch the code in one place only.

In the TArray<TItemEx> version you touch the code in thousands of places maybe.

  • Like 1

Share this post


Link to post
57 minutes ago, Rollo62 said:

I can see also benefit in maintaining code.

Imaging you change


TItemArray = TArray<TItem>;

to


TItemArray = TArray<TItemEx>;

In the TItemArray version you touch the code in one place only.

In the TArray<TItemEx> version you touch the code in thousands of places maybe.

Now you have the type named incorrectly in thousands of places. It should be called TItemExArray. 

 

Seems far worse to me. 

  • Like 2

Share this post


Link to post
4 hours ago, Rollo62 said:

I can see also benefit in maintaining code.

Imaging you change


TItemArray = TArray<TItem>;

to


TItemArray = TArray<TItemEx>;

In the TItemArray version you touch the code in one place only.

In the TArray<TItemEx> version you touch the code in thousands of places maybe.

This happens to me quite often, start with new feature and after a while I see the different naming would be more suitable. So, now changing an alias will be so much easier, because refactoring/rename of type doesn't always work across multiple units.

Share this post


Link to post
8 minutes ago, Mike Torrettinni said:

This happens to me quite often, start with new feature and after a while I see the different naming would be more suitable. So, now changing an alias will be so much easier, because refactoring/rename of type doesn't always work across multiple units.

Then you end up with different names for the same thing. How can that be better? 

 

Refactoring tools get these names changed very reliably. 

 

I don't understand why people are scared of changing names. If you aren't prepared to change names then your code will be a mess.

  • Like 6

Share this post


Link to post

@David Heffernan

Which refactoring tool do you use that reliably will change some scenario where you have lots of places across lots of units where you had something like

 

procedure SomeProc

var

  myList:TList<TFoo>

begin

  myList:=TList<TFoo>.Create

 

then you want to change all places from TList<TFoo> to TObjectList<TFoo>. So the refactor would presumably change the variable declarations and the .Create calls across all the units. It really does not work reliably for us and we have lots of search..replace with invetiable 10 minutes of compiling churn to get it all right.

 

We tend to do TFooList=TList<TFoo> and just use TFooList everywhere for this reason. If the refactoring tools worked well, it would make that less necessary.

  • Like 1

Share this post


Link to post
50 minutes ago, David Heffernan said:

Refactoring tools get these names changed very reliably.

I envy you. I don't have the same experience as you do, renaming across multiple units regularly fails on my Delphi 10.2.3, unfortunately.

It's probably my project design contributing to the issue, but still, the IDE should be managing this better or alert on complex unit design if it bothers it.

 

Share this post


Link to post
1 hour ago, Mike Torrettinni said:

I envy you. I don't have the same experience as you do, renaming across multiple units regularly fails on my Delphi 10.2.3, unfortunately.

It's probably my project design contributing to the issue, but still, the IDE should be managing this better or alert on complex unit design if it bothers it.

 

In 10.3.3, it is better, usually rename finds all places, but sometimes it does not and finds for example just 10 of 30.

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×