多维数组的泛型初始化

本文关键字:泛型 初始化 数组 | 更新日期: 2023-09-27 18:26:03

我有一个多维数组,我想以一种简单快速的方式初始化它:

double[,,] arr = new double[4,5,6];
// doesn't work by design
foreach(double d in arr)
   d = ... ; // my initialization value

这显然是行不通的。但是我想有一个通用函数,用于将所有数组值设置为选定的默认值。使用自己的类,我可以编写一个特殊的构造函数,但是对于值类型,我并不真正了解。使用 C++,我可以通过一个 for 循环以线性方式访问所有项目,但在 C# 中,我认为我必须使用与维度一样多的 for 循环。我目前没有更好的解决方案(或者我正在使用不安全的代码和指针算法,这可能会起作用。

有没有更优雅的方法呢?

多维数组的泛型初始化

不太确定它是否是你想要的,但下面的扩展方法将允许您初始化数组中的每个值,而不考虑维数。

public static class ArrayExtensions
    {
        public static void Set<T>(this Array array, T defaultValue)
        {
            int[] indicies = new int[array.Rank];
            SetDimension<T>(array, indicies, 0, defaultValue);
        }
        private static void SetDimension<T>(Array array, int[] indicies, int dimension, T defaultValue)
        {
            for (int i = 0; i <= array.GetUpperBound(dimension); i++)
            {
                indicies[dimension] = i;
                if (dimension < array.Rank - 1)
                    SetDimension<T>(array, indicies, dimension + 1, defaultValue);
                else
                    array.SetValue(defaultValue, indicies);
            }
        }
    }

像这样使用:

int[, ,] test1 = new int[3, 4, 5];
test1.Set(1);
int[,] test2 = new int[3, 4];
test2.Set(1);
int[] test3 = new int[3];
test3.Set(1);

我强烈建议使用一维数组,并按顺序映射值。您需要从 indeces ijk...转换为正确的数组索引,这是通过下面的 Que() 函数完成的,该函数是泛型数组类 SeqArray<T> 的一部分。

// Test code first
class Program
{
    static void Main(string[] args)
    {
        /* 3 pages, of a 4x2 matrix
         * 
         *         |16 17|
         *      | 8  9|19|
         *   | 0  1|11|21|
         *   | 2  3|13|23|
         *   | 4  5|15|
         *   | 6  7|
         *   
         *  shown above are the sequential indeces for a rank 3 array
         */
        SeqArray<double> arr = new SeqArray<double>(3, 4, 2);
        // Initialize values to squential index "num"
        int num = 0;
        for (int i = 0; i < 3; i++)
        {
            for (int j = 0; j < 4; j++)
            {
                for (int k = 0; k < 2; k++)
                {
                    arr[i, j, k] = num++;
                }
            }
        }
        // Check that the array values correspond to the index sequence
        num = 0;
        for (int i = 0; i < 3 * 4 * 2; i++)
        {
            Trace.Assert(arr.InnerArray[i] == num++);
        }
        // Initialize with value=π
        arr = new SeqArray<double>(Math.PI, 4, 5, 6);
    }
}
public class SeqArray<T>
{
    T[] values;
    int[] lengths;
    public SeqArray(params int[] lengths)
    {
        this.lengths = lengths;
        int N = 1;
        for (int i = 0; i < lengths.Length; i++)
        {
            N *= lengths[i];
        }
        values = new T[N];
    }
    public SeqArray(T value, params int[] lengths) : this(lengths)
    {
        for (int i = 0; i < values.Length; i++)
        {
            values[i] = value;
        }            
    }
    public int[] Lengths { get { return lengths; } }
    public int Size { get { return values.Length; } }
    internal T[] InnerArray { get { return values; } }
    public int Que(params int[] indeces)
    {
        // Check if indeces are omited like arr[4] instead of arr[4,0,0]
        if (indeces.Length < lengths.Length)
        {
            // Make a new index array padded with zeros
            int[] temp = new int[lengths.Length];
            indeces.CopyTo(temp, 0);
            indeces = temp;
        }
        // Count the elements for indeces
        int k = 0;
        for (int i = 0; i < indeces.Length; i++)
        {
            k = lengths[i] * k + indeces[i];
        }
        return k;
    }
    public T this[params int[] indeces]
    {
        get { return values[Que(indeces)]; }
        set { values[Que(indeces)] = value; }
    }
}

这是一个非递归版本,可以替代上面Andy Holt发布的版本:

    public static void SetAll<T>(this Array array, T value)
    {
        var sizes = new int[array.Rank];
        sizes[array.Rank - 1] = 1;
        for (var d = array.Rank - 2; d >= 0; d--)
        {
            sizes[d] = array.GetLength(d + 1)*sizes[d + 1];
        }
        for (var i = 0; i < array.Length; i++)
        {
            var remainder = i;
            var index = new int[array.Rank];
            for (var d = 0; d < array.Rank && remainder > 0; d++)
            {
                index[d] = remainder / sizes[d];
                remainder -= index[d]*sizes[d];
            }
            array.SetValue(value, index);
        }
    }