ILNumerics - Technical Computing Tools

High Performance Framework for Visualization

and Computing in Industry and Science



Array<T> as Class Members

For larger frameworks you might want to store arrays in classes. Take the following naive approach:

If we run this...

...we get an exception. Why?


ILNumerics® Arrays as Class Attributes

ILNumerics arrays can be used as class attributes as long as the following rules are met:

  1. The attribute must be declared as readonly Array<T>.
  2. Initialization of the attribute must utilize a special function: ILMath.localMember<T>().
  3. Assignments to the local variable must utilize the Array<T>.a property (.Assign() function in VB)
  4. Classes with local array members should implement the IDisposable interface.

By applying the rules 1..3, the corrected example displays:

This time, we get, as expected:

Don't worry if you don't understand all of the details yet! This scheme will work out fine if blindly followed.


Detailed Explanation

1. The attribute must be declared as readonly Array<T>.

Why should we use anything else than a local array, right? But what is the readonly specifier for? It prevents us from accidentally assigning values to the array. Continue reading in order to understand, why this is important.

2. Initialization of those types must utilize a special function: ILMath.localMember<T>.

A fundamental mechanism of the ILNumerics memory management is related to the specific lifetime of an array types. All functions return temporary arrays (RetArray<T>) which only exist for exactly one use. After the first use, they will automatically be disposed (rather: they dispose themselves). In order to make use of such arrays multiple times, one needs to assign them to a local variable. This is the place, where they get converted and the underlying storage is efficiently taken over to the local, persistent array variable.

At the same time, we need to make sure, the array is released after the current ILNumerics scope (using (Scope.Enter())) { … }) was left. For this purpose, the conversion to a local array is used. Since there will be a new array out there, we track the new array for later disposal in the current scope. This tracking is done during the conversion to a persistent local Array<T>.

If a current scope exists and once it is left, it does exactly what it promised: it disposes all arrays created since its creation. Now, local array members require a different handling. They commonly live for the lifetime of the class – not of the current ILNumerics scope. In order to prevent local arrays to bedisposed after leaving the scope inside the constructor body, we need something else.

The ILMath.localMember() function does not return a temporary array, but a local array. In fact, the function is quite simple. All it does is create a new Array<T> and return it. Since the types of both sides of the assignment now match, no conversion occurs. The new array is not registered in the current scope, hence it will not be disposed – exactly what we need!

There are other arrays than Array<T>. What if we need a logical array or a cell as class member?

The above code shows how to initialize Array, Logical arrays and Cell arrays in a class context. Now, how do we assign the return value from any function to the local array? Here, the next rule kicks in:

3. Assignments to the local variable must utilize the .a property.

Assigning to a local array directly would activate the disposal mechanism described above. Hence, in order to prevent this for a longer living class attribute, a variable has to be assigned a value by using the .a property. In Visual Basic, the .Assign() function does the same. This will prevent the array from getting registered in the scope.

4. Classes with local array members should implement the IDisposable interface.

Now, that we achieved that our local array attribute is not disposed magically, we – for the sake of completeness – should make sure, it is disposed somewhere. The recommended way of disposing  things in .NET is … the IDisposable interface. In fact, for most scenarios, IDisposable is not necessary since we still have the GC in the back. But we recommend implementing IDisposable, since it makes a lot of things more consistent and error safe. However, we provide the IDisposable interface for convenience reasons only – we do not rely on it like we would for the disposal of unmanaged resources. Therefore, a simplified version is sufficient here and one can omit the implementation of a finalizer method for the class in most cases.

Properties and Array<t>

Having Array<T> as an attribute is certainly nice. Even nicer is the capability of providing the data stored in the array as property to users of the class. Here is how it's done:

  1. Declare a property of the type RetArray<T>.
  2. In the get {} accessor, simply return a clone of the local attribute: return myLocal.C.
  3. In the set {} accessor, again assign the local attribute a value using Array<T>.a

Note that changes to the array returned will not change the attribute stored in the class! Rather a lazy clone is returned and the data will be copied on the first attempt to modify the returned array. So both arrays are not attached – the property implements the same 'Value Semantic' scheme like all functions in ILNumerics do.


Example ILNumerics® Array Utilization Class

Here comes a full featured class example with all rules implemented. The property initializes the array in a lazy fashion: the array will be created at the first request only. Since we cannot test on null, we simply test, if the array is empty. Also, because this is what ILMath.localMember<T> creates.

For the user of your class, this yields two big advantages: one can – without knowing the details – clean up its storage easily. And one can trust that no side effects occur when working with data coming from or going to the class.

Utilizing Array & Co. in User Controls

In the above example code you may have noticed the existence of another subtlety: All properties returning a type from ILNumerics are marked with the DesignerSerializationVisibility attribute:

The utilization of the attribute prevents the following problems from emerging. It must be mentioned that those are not only specific issues concerning ILNumerics, but rather exist for all types of strongly named assemblies used as public properties on user controls in the contect of Visual Studio Designer.

  • When your class is a control / user control and is opened in the designer, the designer will create default values for all public properties of the control.
  • Those values are stored in the *.resx file of the form and incorporated into the InitializeComponents() method in the *.Designer.cs file.
  • Since the types exposed originate from a strongly typed assembly, a fully qualified assembly reference is stored with the value. This reference goes right into the *.resx resource file as a base64 encoded string.
  • The problems start as soon you get an update for your strongly typed assembly (here: ILNumerics.dll). While it is easy to replace the assembly reference in your project, the reference embedded into the resx file remains! As a consequence, the designer refuses to open those forms / controls, which still point to the old assembly.

In order to prevent those issues from happening, simply use the DesignerSerializationVisibility attribute with a Visibility value of Hidden - from the start! It is important to not forget to flag the attribute before the control is opened in the designer for the first time. Otherwise, you will have to manually undo the changes made by the designer later.