References in EGL
mheitz 2700002Y0N Visits (4311)
The mystery of the lost changes
Not long ago, I was asked to help solve a mystery. A developer had stored a record in a dictionary, and he wondered why changes to the record were being lost. His code looked like this:
// Declare the Dictionary.Later, he got the record out of the dictionary again, like this:
rec2 DataRecord = newData.key as DataRecord;When he examined rec2.quantity, he was surprised. It still had its default value, as if the assignment to dataRec.quantity had never happened. Do you know why?
Solving the mystery
This happened because the variable rec is a record, and a record is not a reference type. When you assign one record to another, as in the statement dataRec DataRecord = newData.key as DataRecord;, the data of the fields are copied. This may be obvious to some of you, but it's a foreign concept to developers used to a language like Java where everything is a reference. Here's another way to write the lines which get the record from the dictionary and update it.
// Get the Record from the Dictionary, then update it.This is exactly what the original code does, but now it's clear that the record in the dictionary won't change.
Values and references
In EGL, there are two kinds of types: values and references. Value types are for "normal" variables. They store a piece of data and they're not pointers. When you assign one value to another, the data is copied. Most types in EGL are value types, such as int, char, and records.
A reference is a kind of pointer, but unlike pointers in most programming languages, you don't have to use any special syntax to get or set what it's pointing to. When you assign a reference to another reference, only the pointer is copied. That means both references will be pointing to the same value. EGL's reference types include any, dictionary, and arrays. (Note: fields within a structured record that look like arrays are actually occurred items. Unlike arrays, they have a fixed size and they're not references.)
Mixing values and references
Can you ever assign a reference to a value, or a value to a reference? Yes, in one case. EGL's any type is a reference type, and it's compatible with everything, even values. When you assign a value to an any, we make the any point to a copy of the original value. When you assign an any to a value, another copy is made: the value is assigned a copy of whatever the any is pointing to.
Copying values and how to avoid it
Let's return to the example from the start of this blog entry. The type of a dictionary's value is any. So it's not correct to say that the record is stored in the dictionary. Actually, the dictionary stores a copy of the record. Then, when we pull the record out of the dictionary, it's copied again. Two copies! And another copy every time you get the record! If the record is large then this might not perform well.
You can store your data in a reference variable to reduce the amount of copying. One way to do this is to get rid of the record, and store your data directly in the dictionary. In other words, rather than putting data into record.fieldA, record.fieldB, etc., put the data into dictionary.fieldA, dictionary.fieldB, etc. Now you can access each field individually. You can get the value of a particular field without EGL making a copy of the entire record.
// Create a record with two fields, and a dictionary.After:
// Create a dictionary with two fields.EGL is still making a copy of fieldA and fieldB when you get them from the dictionary. The difference is that only the field you want is copied.
You can achieve the same thing by using an array of records, and storing the array in the dictionary. Since an array is a reference, it's not copied during assignments. Here's how to do it:
// Create an array of records and a dictionary.
Keep in mind the standard warning for every recommendation about performance: your mileage may vary. These techniques make your code a little more complicated, so before you use them do some analysis to ensure they make a significant difference.