以编程方式声明任意等级数组

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

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

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

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

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

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

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

使用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 

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

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

当使用带有Set/GetValue整数时,可以通过必要的装箱/取消装箱轻松解释这一点。

您可以使用类似reflection的方法:

  • 通过调用Array.CreateInstance创建数组实例。 这将为您提供Array类的实例。
  • 调用SetValue为数组赋值。
  • 使用GetValue检索数组的任何元素。

您可以根据现有数组填充长度数组(每个等级一个),然后使用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属性,但它可能只是在逃避我。

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