Defining records in Delphi - (Record as type vs Record as variable) - Differences, cons and pros..?
Asked Answered
P

4

5

I've been doing some research over records in Delphi, and in most cases I see them used as Type, and then declared a new variable of it, such as:

type
  TMyRecord = record
    data1: sometype;
    data2: sometype;
  end;

var
  OnesRecord: TMyRecord;

For arrays, it's also used as:

type
  TMyRecord = record
    data1: sometype;
    data2: sometype;
  end;

var
  OnesRecord: array of TMyRecord;

My questions are the following:

Why one shouldn't use the first approach for arrays, in a way as:

type
  TMyRecord = array of record
    data1: sometype;
    data2: sometype;
  end;

var
  OnesRecord: TMyRecord;

Also, what would be the difference if one would use records defined directly as variables, such as:

var
  OnesRecord: record
    data1: sometype;
    data2: sometype;
  end;

or

var
  OnesRecord: array of record
    data1: sometype;
    data2: sometype;
  end;

?

One final thing; if one would merge the second and third approach, and have type of array, and variable of array of that type, would it be any different than having array of array?

So, is this:

type
  TMyRecord = array of record
    data1: sometype;
    data2: sometype;
  end;

var
  OnesRecord: array of TMyRecord;

any different than this:

type
  TMyRecord = record
    data1: sometype;
    data2: sometype;
  end;

var
  OnesRecord: array of array of TMyRecord;
Procurable answered 26/11, 2014 at 2:18 Comment(12)
I really don't see why would this deserve downvoting, unless already asked somewhere where I haven't noticed...Procurable
Other than RTTI generation (or lack of it), there is no difference between declaring a variable of an explicit record/array type versus declaring a variable with inline record fields. Memory layout and compiler-managed logic are the same either way.Hyoscyamus
so basically all of the examples above (apart from the array part) are doing the same? Any benefit from using specific model...?Procurable
Hard to see what you're asking about. It depends on your particular needs only.Semivitreous
@JustMarc: yes, they are all doing the same thing. It is just a matte of personal preference, the compiler doesn't care much one way or the other. Although if you are going to be passing records around your code, it makes more sense to use explicit types.Hyoscyamus
@FreeConsulting I was asking the questions mentioned in the post; Mainly: are there any differences at all in declaring the records, and why would it be better to use one approach over another..?Procurable
@JustMarc, your question says nothing about your needs. You even can squeeze everything into the single variable declaration if it suits your needs and you're happy with doing that. There are no different approaches, really.Semivitreous
@FreeConsulting exactly my point of the question. I was asking for general use, not specific for my needs. Therefore I haven't specified any particular usage.Procurable
Here is all-in-one complex matrix variable declaration: var Matrix: array of array of record Re, Im: Real end; Do you need complex scalar type or column vector type? Then extract particular component. Otherwise it's fine to leave as is.Semivitreous
@FreeConsulting I don't really get what you're implying by your last comment, but if you look at the bottom answers -> these are what I was looking for.Procurable
Declaring compound variables or compound types of arrays will bite you sooner or later. Better to do it "right" from the beginning.Mise
@JustMarc, my last comment has no hidden messages, just an example to examine. Accepted answer still urges you to step back from general highs and to present a specific use case.Semivitreous
D
4

There's nothing wrong doing what you're doing, just like there's nothing keeping you from overloading the Add() operator to do multiplication.

Your questions are entirely theoretical, and so it's hard to say anything one way or another. Yes, the constructs are "legal". So what?

It's like asking about walking on your hands vs. walking on your feet after you've discovered that you CAN walk on your hands.

The question in all cases is the same ... why would you want to do it?

Rather than asking about coding approaches that are "legal" but nobody uses, why don't you try showing an example where such uses would prove to be an advantage?

From a theoretical standpoint, you're trying to mash-up two different data aggregation mechanisms.

Arrays are a way of collecting items of the same type. Records and classes are a way of collecting related items of different types. They're both containers of sorts, but they have distinct qualities.

You usually declare an array as "array of " not "array of record ... end".

By convention, you define the record as a TYPE so you can "re-use" that type.

You obviously haven't got much experience with this, because you can't actually USE these constructs outside of a very narrow setting.

Let's say you want to define something like "array of integer"; you can define it as a specific type:

type
 TIntArray = array of integer;

Why would you do this? Because at times you'll discover that declaring two different things both as "array of integer" makes them not type-compatible, which is counter-intuitive.

But if they're each declared as TIntArray, the compiler accepts that.

I don't know if this is the correct terminology or not, but as far as the compiler is concerned, there IS a difference between "myvar : " and "myvar : ". In this case, would be "TIntArray", and would be "array of integer".

var
  myvar1 : array of integer;
  myvar2 : TIntArray;

These two are NOT type compatible in all cases. Furthermore,

procedure myproc( AVar : array of integer )

will not accept myproc(myvar2) because myvar2 is not of type "array of integer". Really! It's of type TIntArray. See the difference?

Now replace that declaration with any sort of "record ... end", or even your "array of record ... end" thingie and you start to see the limitations of what you're asking. Yes, the compiler digests it just fine. It just won't let you pass anything in as an argument that matches that type! Even if they "look" exactly the same, believe it or not.

So, to answer your own question, I challenge you to construct a non-trivial example where the things you propose actually make sense and the compiler accepts them. Because while the compiler might accept the isolated examples you've created above, they're not likely to work very well in practice (if at all).

But you get an 'A' for Audacity in asking an insightful question!

Dore answered 26/11, 2014 at 5:58 Comment(5)
These are indeed theoretical, but I still can't see why would that matter; For the question of why would I want to do this - I'm not trying to declare array as array of record - end; I need and have multiple elements of different types, all provided in a record, because I need them so. And it all works fine. That aside, I didn't want to provide direct example of my records, as it's trivial, since the root of my question was just what I asked : is there a difference between declaring it as type or as variable. And your answer has just proven that it's not.Procurable
You mentioned what I haven't thought about before, and from there on I see the difference and issues that might occur. The fact that passing these variables of different types won't be accepted, as in your example of procedure myproc ( AVar: array of integer), is the answer I was looking for. As per your last paragraph -> "they're not likely to work very well in practide (if at all)" -> That was exactly my question! If so, and why?Procurable
"...will not accept myproc(myvar2)..." – that's where I beg to disagree. array of whatevertype in parameter declaration is an open array specification. A procedure declared as myproc( AVar : array of integer ) will accept any array of integer, whether static or dynamic, named or unnamed. So, it would accept either of the two variables. (But if declared as myproc( AVar : TIntArray ), it would accept myvar2 only.) Your illustration would work if you used static arrays, although I admit that it would be slightly less relevant to the question, which uses dynamic arrays as examples.Pasture
Well, you may be right, although it might apply only in limited situations. The point is, it's inconsistent and confusing enough that it's a practice best avoided, IMHO. I'm still waiting for someone (the OP or anybody else) to show an example of what the OP asked that is even useful in practice, because I cannot see any practical use other than highly restricted situations.Dore
Your point is clear to me and I perfectly agree with it. My point is that array of Type in a parameter declaration is not the same thing as array of Type in a variable declaration and your illustration does not work. The statement that procedure myproc( AVar : array of integer ) will not accept myproc(myvar2) because myvar2 is not of type "array of integer" is wrong. This applies specifically to array of .... So, it's just a matter of poor illustration of your otherwise correct point.Pasture
C
5

It is quite a simple issue. When written like this

type
  TPoints = array of record
    X, Y: Double;
  end;

you are unable to declare variables or parameters that operate on a single element of the array. If you never need to operate on a single item then the above declaration can be reasonable.

Every other aspect of your question is logically the same. Be it arrays of arrays, records containing records, anonymous record and arrays as variable types. The issues and design considerations are just the same.


This question is perhaps a little subjective. In my experience, using an inline or anonymous type as shown above, very often turns out to be a bad choice. Often, at some point of future development, it becomes clear that the anonymous type needs to be named.

So myself, I never use compound types composed from anonymous types. And this way I'm always able to refactor code to use small methods operating on on individual elements. There's never a false constraint pushing me towards large methods operating on the compound type that cannot be decomposed.

So I'd declare that type above as:

type
  TPoint = record
    X, Y: Double;
  end;
  TPoints = array of TPoint;

Although in modern Delphi it is much better for type compatibility reasons to omit TPoints and use TArray<TPoint> instead.

Cocainism answered 26/11, 2014 at 7:51 Comment(3)
Well, it made one thing clear: it may be that defining it as "array" is not important as to where it is declared as such, but it looks like there actually IS a difference between declaration as type or as var; Just to be clear: Your second example provides both TPoint and TPoints as types, or is TPoints a variable...?Procurable
@JustMarc They are both types. My point is that if you don't name the types that are used to define compound types, you have less flexibility. You can no longer identify the types you used to make your compound types and that limitation, in my experience, regularly bites you.Cocainism
Sometimes you simply need to refer to the constituent type and so have to change the code to name it. More insidiously, if you don't name the constituents, then you are pushed into writing larger methods than perhaps you feel like. You may wish to apply an extract method refactoring but are unable to because the required type cannot be named. So you simply decide not to extract the method. This is particularly insidious because you are now in a position where arbitrary factors are constraining your desire to write clean well factored code.Cocainism
O
4

Using a Type declaration lets you easily use it in multiple other types or variables and to pass around variables or constants of that type to procedures and functions.

Type 
  TCatRecord = Record
    Name  : string;
    Color : string;
  End;
  TCatArray = Array of TCatRecord;
Var
  MyCat : TCatRecord;
  NeighbourhoodCats : TCatArray;
...
Procedure SpayACat(aCat: TCatRecord);
Function PickCutestCat(SomeCats: TCatArray): TCatRecord;
...
MyCat := PickCutestCat(NeighbourhoodCats); 
Outdate answered 26/11, 2014 at 5:49 Comment(1)
Bravo for simple and clean answer!Procurable
D
4

There's nothing wrong doing what you're doing, just like there's nothing keeping you from overloading the Add() operator to do multiplication.

Your questions are entirely theoretical, and so it's hard to say anything one way or another. Yes, the constructs are "legal". So what?

It's like asking about walking on your hands vs. walking on your feet after you've discovered that you CAN walk on your hands.

The question in all cases is the same ... why would you want to do it?

Rather than asking about coding approaches that are "legal" but nobody uses, why don't you try showing an example where such uses would prove to be an advantage?

From a theoretical standpoint, you're trying to mash-up two different data aggregation mechanisms.

Arrays are a way of collecting items of the same type. Records and classes are a way of collecting related items of different types. They're both containers of sorts, but they have distinct qualities.

You usually declare an array as "array of " not "array of record ... end".

By convention, you define the record as a TYPE so you can "re-use" that type.

You obviously haven't got much experience with this, because you can't actually USE these constructs outside of a very narrow setting.

Let's say you want to define something like "array of integer"; you can define it as a specific type:

type
 TIntArray = array of integer;

Why would you do this? Because at times you'll discover that declaring two different things both as "array of integer" makes them not type-compatible, which is counter-intuitive.

But if they're each declared as TIntArray, the compiler accepts that.

I don't know if this is the correct terminology or not, but as far as the compiler is concerned, there IS a difference between "myvar : " and "myvar : ". In this case, would be "TIntArray", and would be "array of integer".

var
  myvar1 : array of integer;
  myvar2 : TIntArray;

These two are NOT type compatible in all cases. Furthermore,

procedure myproc( AVar : array of integer )

will not accept myproc(myvar2) because myvar2 is not of type "array of integer". Really! It's of type TIntArray. See the difference?

Now replace that declaration with any sort of "record ... end", or even your "array of record ... end" thingie and you start to see the limitations of what you're asking. Yes, the compiler digests it just fine. It just won't let you pass anything in as an argument that matches that type! Even if they "look" exactly the same, believe it or not.

So, to answer your own question, I challenge you to construct a non-trivial example where the things you propose actually make sense and the compiler accepts them. Because while the compiler might accept the isolated examples you've created above, they're not likely to work very well in practice (if at all).

But you get an 'A' for Audacity in asking an insightful question!

Dore answered 26/11, 2014 at 5:58 Comment(5)
These are indeed theoretical, but I still can't see why would that matter; For the question of why would I want to do this - I'm not trying to declare array as array of record - end; I need and have multiple elements of different types, all provided in a record, because I need them so. And it all works fine. That aside, I didn't want to provide direct example of my records, as it's trivial, since the root of my question was just what I asked : is there a difference between declaring it as type or as variable. And your answer has just proven that it's not.Procurable
You mentioned what I haven't thought about before, and from there on I see the difference and issues that might occur. The fact that passing these variables of different types won't be accepted, as in your example of procedure myproc ( AVar: array of integer), is the answer I was looking for. As per your last paragraph -> "they're not likely to work very well in practide (if at all)" -> That was exactly my question! If so, and why?Procurable
"...will not accept myproc(myvar2)..." – that's where I beg to disagree. array of whatevertype in parameter declaration is an open array specification. A procedure declared as myproc( AVar : array of integer ) will accept any array of integer, whether static or dynamic, named or unnamed. So, it would accept either of the two variables. (But if declared as myproc( AVar : TIntArray ), it would accept myvar2 only.) Your illustration would work if you used static arrays, although I admit that it would be slightly less relevant to the question, which uses dynamic arrays as examples.Pasture
Well, you may be right, although it might apply only in limited situations. The point is, it's inconsistent and confusing enough that it's a practice best avoided, IMHO. I'm still waiting for someone (the OP or anybody else) to show an example of what the OP asked that is even useful in practice, because I cannot see any practical use other than highly restricted situations.Dore
Your point is clear to me and I perfectly agree with it. My point is that array of Type in a parameter declaration is not the same thing as array of Type in a variable declaration and your illustration does not work. The statement that procedure myproc( AVar : array of integer ) will not accept myproc(myvar2) because myvar2 is not of type "array of integer" is wrong. This applies specifically to array of .... So, it's just a matter of poor illustration of your otherwise correct point.Pasture
H
1

Just to complement the choosen answer, here we go a not so well documented feature for global vars in unit:

Declaration of array of records variables with initialized values

type
  TPatch = Packed Record
    Pos: NativeUInt;
    Old: Byte;
    Sup: Byte;
  End;

var
  OpCodes: Array [1..5] of TPatch = (
    (Pos: $044773CF; Old: $7D; Sup: $EB),
    (Pos: $04477400; Old: $0D; Sup: $0B),
    (Pos: $0447D370; Old: $E3; Sup: $E9),
    (Pos: $0447D371; Old: $01; Sup: $99),
    (Pos: $0447D372; Old: $13; Sup: $00),
  );

Now you can use

Howenstein answered 14/4, 2018 at 13:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.