Tuesday, 17 June 2014

Two singularly complex problems - Arrays and Lists

The delightful aspect of programming in C# is the large number of possibilities, to resolve a problem, available to a programmer in the form of different forms of implementations across versions of the underlying technology and framework.

It is delightful because two different programmers may provide a solution to the same problem, without differing in technique, because the implementation of the same language feature across the different versions of the C# language compiler are different.

It can be torrid, though, if you do not enjoy the intricacies of working with different types in the CLR and/or the underlying unmanaged types of the Windows platform.

1. Unmanaged array - the 'mysterious' object[*]

The Microsoft.Office.Interop,Excel Chart object presents a challenge in the way it exposes the Chart's series collection's data values.

For instance, if you want to read the Chart's Source Data range/Data Table, the problem is in the way the series collection exposes each series' data values. Being a System.ComObject, the series.Values (that represent each Chart Series values) returns an unmanaged array !! What is so surprising, you may ask?

The surprise is that you may convert it to an object array but may inadvertently leave out testing it as it will compile ok. At runtime, though, an error that an object of type [,] cannot be converted to an object of type [] will be thrown!

The exasperating aspect of this error has to be experienced to really appreciate the nature of the error !!

The reason it is exasperating is because you may try creating an Array with CreateInstance and while it should work, at a glance,

var array = System.Array.CreateInstance(
                            new int[] { totalColumns },
                            new int[] { totalRows });
array = series.Values;

it does not !

The solution is to iterate through the series.Values as below:

// --- 1. Get ChartObjects in worksheet
//-- 2. iterate through the chart object's series collection

foreach (object seriesValue in series.Values as System.Array)
double d = (double)seriesValue;

The trick is to convert the unmanaged array values into an array while iterating through it !

Best Practice
3. If you are using a System.ComObject in a for... or foreach... loop make sure to release the COM object with Marshal.ReleaseComObject in the System.Runtime.Interopservices namespace in the finally block of a try...catch as below:

foreach (..... ComObject in ...){
if (object!=null)

Remember, you cannot set a ComObject used in a loop to null ! A runtime exception will be thrown if you attempt to do so.

2. Compare two Lists or Arrays

The option to obtain the iterated value into an array or a list is as per your requirement.

In both the cases, you may want to work with both the array or the list. 

Below are two techniques to compare, using, first, the TrueForAll Linq method (make sure to add the System.Linq namespace) as below.

                            var list = new System.Collections.Generic.List();
                            var list1 = new System.Collections.Generic.List();

                                            if (list.TrueForAll(list1.Contains) && list1.TrueForAll(list.Contains))
                                            // --

Or, convert the array into a List and use the System.Array's SequenceEqual check for an array,

object[] rangeValueArray = (from item in list select item as object).ToArray();
object[] seriesValueArray = (from item in list1 select item as object).ToArray();

if (rangeValueArray.SequenceEqual(seriesValueArray))
// --
else if (!rangeValueArray.SequenceEqual(seriesValueArray))
// --

This method helps check if the elements are equal plus in sequence !