Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Programmatically Declare Array of Arbitrary Rank

Tags:

arrays

c#

In C#, there are three types of arrays: one-dimensional, jagged, and multi-dimensional rectangular.

The question is: given an array of a specific size, how can we create a new array with the same dimensions and rank?

In the case of a multidimensional rectangular array, there appears to be no syntax by which it is possible to define the size and rank (number of dimensions) at runtime.

C# declares multidimensional arrays with commas in the indexers:

object[,,] myArray = new object[2,4,2];

In the above example, I can determine the shape of the array by invoking the Rank property and the size of each dimension by calling the GetLength method and passing the specified dimension.

However, even though I can determine that myArray is 2 x 4 x 2, how can I programmatically create a new instance of an array with the same dimensions, if I am not given the rank of the array beforehand?

like image 244
nicholas Avatar asked Dec 20 '22 02:12

nicholas


2 Answers

Use Array.CreateInstance(Type, Int32[]) method to create an array of an arbitrary size.

But the problem, you will have after creating this array is: How do you efficiently access the elements of the array if you don't know its rank?

You may use myArray.GetValue(Int32[]) and myArray.SetValue(Object, Int32[]) but I assume the performance is no that good.

To sum up:

public static Array CreateArray(Array array)
{
    // Gets the lengths and lower bounds of the input array
    int[] lowerBounds = new int[array.Rank];
    int[] lengths = new int[array.Rank];
    for (int numDimension = 0; numDimension < array.Rank; numDimension++)
    {
        lowerBounds[numDimension] = array.GetLowerBound(numDimension);
        lengths[numDimension] = array.GetLength(numDimension);
    }

    Type elementType = array.GetType().GetElementType();  // Gets the type of the elements in the input array

    return Array.CreateInstance(elementType, lengths, lowerBounds);    // Returns the new array
}

Update

I've done a little benchmark to compare performance of the array indexer and the GetValue, SetValue versions.

Here is the code I used:

const int size = 10000000;
object[] array1 = new object[size];
object[] array2 = new object[size];

Random random;
random = new Random(0);
for (int i = 0; i < size; i++)
{
    array1[i] = random.Next();
    array2[i] = random.Next();
}

Stopwatch stopwatch = new Stopwatch();

Console.ReadKey();
stopwatch.Restart();
for (int i = 0; i < size; i++)
    array1[i] = array2[i];
stopwatch.Stop();
Console.WriteLine("Indexer method: {0}", stopwatch.Elapsed);

random = new Random(0);
for (int i = 0; i < size; i++)
{
    array1[i] = random.Next();
    array2[i] = random.Next();
}

Console.ReadKey();
stopwatch.Restart();
for (int i = 0; i < size; i++)
    array1.SetValue(array2.GetValue(i), i);
stopwatch.Stop();
Console.WriteLine("Get/SetValue method: {0}", stopwatch.Elapsed);

The result are:

Indexer method: 0.014 s
Set/GetValue method: 1.33 s

The result are slightly different if I replace the int by object.

Indexer method: 0.05 s
Set/GetValue method: 0.54 s

This can be easily explained by the necessary boxing/unboxing when using integer with Set/GetValue.

like image 83
Cédric Bignon Avatar answered Dec 22 '22 15:12

Cédric Bignon


You can use reflection-like methods:

  • Create your array instance by calling Array.CreateInstance. This will give you an instance of the Array class.
  • Call SetValue to assign values to your array.
  • Use GetValue to retrieve any elements of the array.
like image 34
O. R. Mapper Avatar answered Dec 22 '22 16:12

O. R. Mapper