Boxing
Boxing and unboxing are the built-in capabilities of the compiler and runtime to convert value types and descriptor types to or from objects. Note that id is used to define a boxed implied-decimal data type (d.), and ip is used to define a boxed implied-packed data type (p.).
The variable used to hold a boxed value can be declared as either explicitly typed (@a, @d, @i4, etc.) or untyped (@*). Keep in mind that a typed boxed variable is processed and validated at compile-time. To box a value, you must cast it using either the boxing cast syntax (@type) or (object). When you box using the (@type) cast, the value is explicitly boxed as the specified type.
obj = (@d)dvar
When boxing a numeric literal, the expression type defaults to d. Therefore, in order to box an integer or implied-decimal, you need to explicitly cast the literal as (@i4) or (@id). |
When you box using the (object) cast, the type of the expression determines the type inside the box. The runtime creates an object and then puts a copy of the value type into the object. For example,
obj = (object)mystructvar
Boxing occurs automatically in the following circumstances:
- A literal or value type is passed as an argument to a parameter of type System.Object.
- A value type is assigned to a variable of type System.Object.
- A literal is assigned to a field or property of type System.Object. (First the literal is changed to the corresponding .NET type, and then it is boxed.)
When an item is boxed, it is of type @System.Object, so only System.Object’s members are accessible (for example ToString()). Boxing is very important when using the ArrayList classes (System.Collections.ArrayList and Synergex.SynergyDE.Collections.ArrayList), because the ArrayList can only deal with entities derived from System.Object.
If @System.Object=@d, you can only unbox the object to a d, and you must do it explicitly. The object cannot be automatically unboxed because the compiler can’t detect its type. |
In .NET, boxing occurs automatically only when a value type is assigned to a variable or parameter of type object.
An error will occur if you do any of the following:
- Box or unbox a local structure.
- Box a structure that contains a handle.
- Have an explicit box on the left side of an equals sign in an assignment.
- Save a string to a boxed non-CLS structure.
Unboxing
Before you can access (or compare) members of a boxed type, the boxed type must first be unboxed. Unboxing allows access to the value type inside the boxed object.
In most cases, you will need to explicitly cast the result to unbox it. However, the boxed type is automatically unboxed under the following circumstances:
- A boxed type is passed to an unboxed type parameter. The types must match or must both be integer or numeric.
- An assignment is made in which the right side’s type within the box is the same as the left side’s type.
- A boxed structure’s field is accessed.
- A boxed variable is used in certain Synergy statements, such as I/O statements.
- An expression contains boxed operands matching the type of other operands, except in operators.
Automatic unboxing requires the boxed variable to be explicitly typed.
The following example does an automatic unbox on a field access of a boxed structure:
record shnd ,@mystruct svar ,mystruct shnd = (@mystruct)svar x = shnd.fld ;Automatic unbox
However, for an unknown boxed type, such as
obj ,@*
you cannot use automatic unboxing. Instead, you must explicitly cast the variable to unbox it:
obj = shnd x = ((mystruct)obj).fld ;Unbox
In the example below, the automatic box of the variable field creates a new object of type @i4 (the type of variable field) and copies in the value of the variable field:
record ifield, int field, i4 sfield, structure ooi4, @i4 oosfield, @structure oifield, @int . . . ooi4 = (@i4)field oosfield = (object)sfield oifield = (object)ifield field = (i4)ooi4 sfield = (structure)oosfield ifield = (int)oifield
To unbox, the runtime extracts the i4 from the object in the example above and stores it in the variable field. For example,
field = (i4)ooi4 sfield = (structure)oosfield
The reason that we specify @i4 and i4 when we box field and unbox ooi4 is that integer fields (which are usually descriptor types) are often converted to native .NET data types (value types) for optimization: i1 becomes System.Sbyte, i2 becomes System.Int16, i4 and int (which are synonymous in traditional Synergy) become System.Int32, and i8 becomes System.Int64. For the most part, these conversions are seamless; you don’t need to consider them as you code. They can, however, cause problems if you rely on automatic boxing or unboxing. For example, the following code (which works with traditional Synergy) won’t work with .NET because casting ivar as (object) results in an @int, which can’t be unboxed to an (@i4), as shown below. (You can’t unbox one type to another.)
record num, @object ivar, i4 proc num=(object)ivar ;Results in an @int ivar=(i4)num ;Attempts to unbox the @int to an (@i4)
To prevent this, force the data type as you box and/or unbox to ensure you use the same type. For example, the above would work for both traditional Synergy and Synergy .NET if ivar was explicitly boxed using (@i4):
num=(@i4)ivar
or unboxed using (int):
ivar=(int)num