How do I tell which case is valid in a variant record when the case variable isn't named, and how do I create values of such records?
data-structures, delphi, memory-management
Solution
You're not understanding correctly. :-)
The record always has the same layout in memory, and always has both `0` and `1` parts of the `case` section. You don't create one or the other. The one that is used is decided by how you access the record automatically; both cases always exist.
`TRect` is a good example. If you create a new `TRect` using the `Rect` function, you've got a single `TRect` record that has both the `0` and `1` parts available:
var
R: TRect;
RTop, RLeft: Integer;
RTopLeft: TPoint;
R := Rect(0, 0, 100, 100);
RTop := R.Top; // Access TRect.Top separately
RLeft := R.Left; // Access TRect.Left separately
RTopLeft := R.TopLeft; // Access TRect.TopLeft, which is both Top and Left
if RTopLeft.X = 0 // This is true, because we created the R rect with 0, 0
// as the left and top
if RTop = 0 // This is also true, for the same reason
Another example of how this works is directly from the documentation. Your code can use the named tag part of the record (`Salaried`) to determine which part of the variant portion it should access. Note that both parts still exist at the same time, though.
type
TEmployee = record
FirstName, LastName: string[40];
BirthDate: TDate;
case Salaried: Boolean of
True: (AnnualSalary: Currency);
False: (HourlyWage: Currency);
end;
var
Person: TEmployee;
Person.FirstName := 'John';
Person.LastName := 'Smith';
Person.BirthDate := EncodeDate(1980, 1, 1);
Both the `AnnualSalary` and `HourlyWage` exist in memory; which of the two cases are available depends on which one you access:
// If this person is paid by the hour
Person.Salaried := False;
Person.HourlyWage := 20.00;
// If this person is paid an annual salary instead
Person.Salaried := True;
Person.AnnualSalary := 80000.00;
// You can access both at the same time. Of course, it wouldn't make
// any sense to do so in this case.
if (Person.Salaried and (Person.AnnualSalary > 60000)) then
DoSomething
else if (not Person.Salaried) and (Person.HourlyWage > 15.00) then
DoSomethingElse;
The `tag` name (as pointed out by David Heffernan and Free Consulting in the comments) takes space in the memory allocation.
program TestRec;
{$APPTYPE CONSOLE}
uses
SysUtils;
type
TTestNoTagName = record // No tag name on variant part
I: Integer;
case Boolean of
True: (X, Y: Integer);
False: (A, B: Integer;)
end;
type
TTestWithTagName = record // Tag name on variant part
I: Integer;
case Dummy: Boolean of
True: (X, Y: Integer);
False: (A, B: Integer);
end;
begin
WriteLn('TTestNoTagName: ', SizeOf(TTestNoTagName)); // SizeOf(12)
WriteLn('TTestWithTagName: ', SizeOf(TTestWithTagName)); // SizeOf(16)
ReadLn;
end.
(And thanks to @davea for spotting the mistake in my initial test code.)
Problem
I've been reading about "case" within records and I still have some open questions about it. (http://www.delphibasics.co.uk/Article.asp?Name=Records) I use the example: ``` type TRect = packed record case Integer of 0: (Left, Top, Right, Bottom: Integer); 1: (TopLeft, BottomRight: TPoint); end; ``` The example basically tells me that the first member/property of `TRect` is an `Integer` (which this `Integer` has no name!). If this (unnamed!) integer is `0` or `1` it just adds the corresponding members: ``` (0 for Left, Top, Right, Bottom) (1 for TopLeft, BottomRight) ``` So let's say there is content in memory which I would like to use this record on. I fairly add a new `type` that represents a pointer to `TRect`. The new record would look like this: ``` type PRect = ^TRect; TRect = packed record case Integer of 0: (Left, Top, Right, Bottom: Integer); 1: (TopLeft, BottomRight: TPoint); end; ``` So the content (in memory) would sorta like this: ``` First Integer = 0 // determines the case! Left = 1 Top = 2 Right = 3 Bottom = 4 ``` This content has a total size of 20 bytes in memory and has the address of X. To output my content I would typecast this address like this: `PRect(X)` Now how do I know my record has the "0" or the "1" case?! How could I also create a record with the case of "0" or "1"? I'm extremely confused by this and would like to know if I understood the use of `case` in records...