【问题标题】:Programmatically Declare Array of Arbitrary Rank以编程方式声明任意秩数组
【发布时间】:2013-07-23 20:33:38
【问题描述】:

在 C# 中,数组有三种类型:一维、锯齿状和多维矩形。

问题是:给定一个特定大小的数组,我们如何创建一个具有相同维度和排名的新数组?

在多维矩形数组的情况下,似乎没有语法可以在运行时定义大小和等级(维数)。

C# 在索引器中用逗号声明多维数组:

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

在上面的示例中,我可以通过调用Rank 属性来确定数组的形状,并通过调用GetLength 方法并传递指定的维度来确定每个维度的大小。

但是,即使我可以确定 myArray 是 2 x 4 x 2,我如何以编程方式创建具有相同尺寸的数组的新实例,如果我没有给出数组的等级提前?

【问题讨论】:

    标签: c# arrays


    【解决方案1】:

    使用Array.CreateInstance(Type, Int32[]) 方法创建任意大小的数组。

    但是,创建这个数组后,你会遇到的问题是:如果不知道数组的等级,如何有效地访问数组的元素?

    您可以使用myArray.GetValue(Int32[]) 和myArray.SetValue(Object, Int32[]),但我认为性能没有那么好。

    总结一下:

    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
    }
    

    更新

    我做了一个小基准来比较数组索引器和 GetValue、SetValue 版本的性能。

    这是我使用的代码:

    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);
    

    结果是:

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

    如果我将int 替换为object,结果会略有不同。

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

    这可以通过Set/GetValue 使用整数时的必要装箱/拆箱来轻松解释。

    【讨论】:

    • 正是我想要的。我想我只是没有使用正确的术语进行搜索。不过,我不太确定SetValue 和GetValue 性能不佳的假设。我想正常的索引操作会在幕后调用类似的方法。
    • @Nicholas 我做了一个小基准测试,结果非常...明显(查看帖子更新)。
    • 读完这篇文章后,我觉得有必要检查一下它是否可以编译:int[] myLengthsArray = new int[2] { 2, 2 }; int[,] array = (int[,]) Array.CreateInstance( typeof(int), myLengthsArray ); 确实可以编译,但它确实打败了整个事情的重点
    • 我很困惑。我认为您的基准测试没有任何问题,但如果您查看referencesource.microsoft.com/#mscorlib/system/array.cs 的第 705-708 行,@nicholas 似乎是正确的?
    【解决方案2】:

    您可以使用类似反射的方法:

    【讨论】:

      【解决方案3】:

      您可以根据现有数组填充一个长度数组(每个等级一个),然后使用Array.CreateInstance(Type, int[])。所以:

      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);
      }
      

      很遗憾,没有一种方法可以一次性返回所有长度,但我看不到。我也很惊讶Array 上没有 ElementType 属性,但它可能只是在逃避我。

      请注意,此不会尝试为每个维度保持与原始数组相同的上限/下限 - 只是大小。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-05-28
        • 1970-01-01
        • 1970-01-01
        • 2012-07-21
        相关资源
        最近更新 更多