Change the innermost element type of a jagged array — any .NET language: C#, F#, VB.NET, MSIL, C++/CLI, etc Write a class containing a method with the signature

System.Array ChangeElementType(System.Array jaggedArray)


(you may rename identifiers ChangeElementType, jaggedArray and shorten the type System.Array with using directives). The class may contain any additional members if needed.

You may assume the following preconditions (and do not need to validate them):


The argument jaggedArray is not null.
The argument jaggedArray is an array instance whose type can be represented in C# notation as int[...]..., i.e. the type int followed by 1 or more rank specifiers [...] where each rank specifier is a pair or square brackets with 0 or more commas between them. For example, it could be an array of type int[], int[,][][,,,], etc. 
No element at any level of the jaggedArray is null.
All elements at any level of the jaggedArray are distinct w.r.t. reference equality (i.e. the same array instance does not appear at several different positions).
The lower bound of any array dimension is 0.
The length of any dimension and the total number of elements of any array fits into the type int.


The method must create and return an array of type long[...]... which is obtained from the type of jaggedArray by replacing int with long and keeping all rank specifiers the same. The result must have the same shape as jaggedArray, i.e. all array dimensions at any level must be the same. All elements at the innermost level must have the same numeric values (cast from int to long). The method must not mutate the argument jaggedArray or any its elements at any level.

The solution may be implemented in any language for the .NET platform: C#, F#, VB.NET, MSIL, C++/CLI, etc. All necessary using, open or imports directives or fully qualified names must be explicitly specified (do not rely on IDE defaults). The only referenced assembly must be mscorlib, no P/Invoke or COM Interop allowed. The solution cannot access any Internet services to get the result, intermediate data or to download parts of an algorithm.

It is a golf — try to minimize the number of bytes in the source.
C# - 396 440 595

I'm not entirely sure why I did this, and I can't help but feel I'm missing something. Rewrote the old c (for copy) method as a for-loop in the ChangeElementType method (f). Unless I find something substantial, I think I'll not be making this any shorter.

Golfed code:

using A=System.Array;using T=System.Type;class P{T g(T o){return o.IsArray?g(o.GetElementType()).MakeArrayType(o.GetArrayRank()):typeof(long);}A f(A a){var R=a;int i,r;for(var x=new int[i=r=a.Rank];;){if(i&lt;1){if(a!=R)return R;R=A.CreateInstance(g(a.GetType().GetElementType()),x);i=1;}if(--i&lt;r)if(x[i]--&gt;0)i+=2;else x[i]=a.GetLength(i);else{var o=a.GetValue(x);R.SetValue(o is A?f((A)o):o,x);}}}}


Readable code with an excuse for a test:

using A=System.Array;
using T=System.Type;

class P
{
    // turns a type like "int[,][]" into "long[,][]"
    T g(T o)
    {
        return o.IsArray?g(o.GetElementType()).MakeArrayType(o.GetArrayRank()):typeof(long);
    }

    // ChangeElementType
    A f(A a)
    {
        var R=a; // R is the result, a gives us the type and a nice default value
        int i,r; // i is a counter, r is the rank

        // this loop first counts backwards filling the x array with the dimension lengths
        // then it does a tree traversal and copies things about
        for(var x=new int[i=r=a.Rank];;) // create the x array, and sets i and r
        {
            if(i&lt;1)
            { // gone off start
                if(a!=R) // been here before
                    return R; // return the result
                R=A.CreateInstance(g(a.GetType().GetElementType()),x); // create the array to fill
                i=1; // move to start of x
            }

            if(--i&lt;r) // move back
                if(x[i]--&gt;0) // decrement index
                    i+=2; // move forward instead
                else 
                    x[i]=a.GetLength(i); // reset index
            else
            {
                var o=a.GetValue(x); // get the int/array
                R.SetValue(o is A?f((A)o):o,x); // if it's an array, Change it, if not, pass it directly (the int)
            }
        }
    }

    // test method (prints True if the code works, might print False if it doesn't)    
    static void Main()
    {
        string old = "";

        var a = new int[10][,];

        for (int i = 0; i &lt; 10; i++)
        {
            a[i] = new int[3,5];

            for (int j = 0; j &lt; 3; j++)
            {
                for (int k = 0; k &lt; 5; k++)
                {
                    int v = i * j * k;
                    a[i][j,k] = v;
                    old += v.ToString();
                }
            }
        }

        long[][,] l = (long[][,])(new P().f(a));

        string nwe = "";

        for (int i = 0; i &lt; 10; i++)
        {
            for (int j = 0; j &lt; 3; j++)
            {
                for (int k = 0; k &lt; 5; k++)
                {
                    nwe += l[i][j,k].ToString();
                }
            }
        }

        System.Console.WriteLine(old == nwe);

        System.Console.ReadKey(true);
    }
}