Programmatically Declare Array of Arbitrary Rank

后端 未结 3 563
走了就别回头了
走了就别回头了 2021-01-14 01:29

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 cr

相关标签:
3条回答
  • 2021-01-14 02:00

    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.

    0 讨论(0)
  • 2021-01-14 02:07

    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.
    0 讨论(0)
  • 2021-01-14 02:08

    You can populate an array of lengths (one per rank) based on the existing array, and then use Array.CreateInstance(Type, int[]). So:

    public static Array CreateNewArrayOfSameSize(Array input)
    {
        int[] lengths = new int[input.Rank];
        for (int i = 0; i < lengths.Length; i++)
        {
            lengths[i] = input.GetLength(i);
        }
        return Array.CreateInstance(array.GetType().GetElementType(), lengths);
    }
    

    It's a shame there isn't a method to return all the lengths in one go, but I can't see one. Also I'm surprised that there isn't an ElementType property on Array, but again it may just be eluding me.

    Note that this doesn't attempt to maintain the same upper/lower bounds for each dimension as the original array - just the sizes.

    0 讨论(0)
提交回复
热议问题