Arrays & Structs in-depth Part I
by
, 3-Sep-2009 at 08:00 AM (8327 Views)
Native arrays have been around in Visual DataFlex for years now, it's obvious that native array types are preferred by far over the old Array class, but just what are the differences? For one, with native array types you can create local array variables without resorting to creating an object. That also means you can use array types as parameter types and return types, and the data is carried across method calls far easier than array objects. You also never have to worry about destroying an object, array variables like any other variable utilize automatic memory management.
Struct types obviously also deprecates the use of the legacy Type/End_Type structures. Anyone who's ever used Type/End_Type structures knows what I'm talking about, what a pain that was to deal with. Native struct types are just infinitely better.
There are other advantages that you may not think of right away, such as interoperability and integrated support for COM arrays and structs, complex structured types with XML web services and SOAP, compatibility with C-style arrays and struct types for improved use of dll calls and using the Win32 API.
In this multi-part series we'll explore structs and arrays more in depth. We'll start with the most simple basic information you need to know.
Arrays
You can use static or dynamic arrays. The only difference is whether the array has a static, fixed size of elements, or if the array can grow dynamically. Dynamic arrays are definitely preferred and should be your first choice. There's really no performance difference, so there's no reason to sacrifice the flexibility of dynamic arrays.
Static arrays have a use when declaring an array member in a struct that must meet certain requirements for compatibility with C-style arrays and structs. Unless you're defining such a struct for use with a DLL for example, you should always use a dynamic array.
Dynamic arrays are created like this:
You can create arrays out of any supported data type. The question about naming convention often comes up, and while there's no real hard requirement, the recommendation and the style we usually(but not always) follow is a good name describing the variable in plural form(or optionally including the word array if it makes more sense), with no specific prefix for designating array.Code:String[] customerNames Integer[] myIntArray Move "Joe" to customerNames[0] Move 1234 to myIntArray[0]
You can use multi-dimensional arrays like this:
You can even have more than two dimensions, but in many cases multi-dimensional arrays should be avoided in favor of struct types. More about that in part II.Code:Integer[][] lotteryNumbers
Dynamic arrays grow automatically as needed during element assignment in a Move statement. Internally this expansion is optimized so it grows in progressively larger chunks, rather than one-by-one, defining the array's internal capacity. Thus adding elements to a dynamic array is typically very fast. This is not to be confused with the actual size of the array, which is never larger than specified. The current array capacity may be larger than the current size.
You can also grow and shrink the array directly, using the ResizeArray() function. This is often used to "clear" the array and remove all elements in one go, resizing the array to zero elements.
In Visual DataFlex 2009 we also introduced two new functions; InsertInArray() which inserts an element at a specified index and expands the array. and RemoveFromArray() which removes the element at a specified index and contracts the array.
Struct Types
Struct types are used to keep related values together like a database record, so you can conveniently pass around the whole record as a parameter or return value for example.
As far as naming conventions, again there's no hard rule but our recommendation is to prefix the struct type with lowercase t, and no specific prefix for designating struct for the variable names, instead use a descriptive variable name that makes it clear what struct type is used. This is often misunderstood, but the basic idea is that for the variable name, the fact that it's a struct variable in general is of little interest compared to which struct type. i.e. conceptually it's more important to convey the information that this variable holds customer information than that this variable is of some sort of struct in general. Prefixing the struct type name with lowercase t will help to reduce the possibility of name clashes with other constructs that are defined globally. Remember, this is only a recommendation that we try follow ourselves(but not always), and should not be taken as a requirement.Code:Struct tCustomerInfo String sFirstName String sLastName Number nBalance End_Struct Procedure Foo tCustomerInfo customer Move "Joe" to customer.sFirstName Move "Johnsson" to customer.sLastName ... End_Procedure
Struct & Array Together in Harmony
You can obviously use struct types and arrays together, that's when it becomes really powerful:
As you can see the possibilities are endless, you can nest structs and arrays and yet keep it organized. With the familiar dot syntax, accessing and referencing deep structures becomes a breeze.Code:Struct tCustomerInfo String sFirstName String sLastName Number nBalance End_Struct Struct tOrderItem String sName Number nPrice End_Struct Struct tOrderInfo tCustomerInfo customer tOrderItem[] orderItems End_Struct Function CreateOrder ... Returns tOrderInfo ....
A little known trick is that you can also easily create tree structures by nesting a recursive struct using array. The value tree structure used internally for web services and xml serialization looks like this:
This works as long as the recursive struct member is a dynamic array. You obviously cannot create direct recursive struct members, as it would create a struct of infinite size. But by using a dynamic array, the size of the member is fixed, and the array is stored indirectly so the size can change dynamically.Code:Struct tValueTree String sValue tValueTree[] children End_Struct
Struct & Array as Parameter and Return Type
Obviously you can create local variables of struct and array type, you can also declare parameters of struct & array as well as return types. This offers an enormous flexibility. Struct and array parameters and return values also work just like any other parameter and return value, you never have to worry about memory management, and parameters are always passed by value, just like any other parameter type.
Now you might think that passing around huge structures and arrays by value would suffer a performance penalty, but it's actually not copying the parameter values, yet retaining copy semantics. This is all accomplished with a built-in copy-on-write optimization, where essentially all struct and array values are passed around by reference internally, until an attempt to modify the value is performed, where upon a copy is first created. All this occurs behind the scenes, and you don't have to worry about it. This even applies to return values, so there's no performance penalty for returning a huge struct or array. In a rare feat you get to have your cake and eat it too. You get the performance, while you also get copy semantics.
In part II we'll look at sorting and searching, and how to choose between using a struct or array.