在 C# 中填充多维数组而不用单值循环

ewi*_*gno 4 c# arrays loops multidimensional-array

C# 中有没有一种方法可以在不使用循环的情况下将多维数组的每个值设置为特定值?我找到了Array.Fill但它似乎只适用于一维数组。

基本上我正在寻找的是这样的:

double[,,,] arrayToFill = new double[7,8,9,10];
Array.FillWhole(arrayToFill, 1.2345);
Run Code Online (Sandbox Code Playgroud)

当然,可以使用嵌套循环,但这看起来很烦人。也许有更好的解决方案?

Mat*_*son 7

(请参阅此答案中的最后一个代码示例以获得最佳解决方案。)

您实际上可以使用 aSpan<T>来执行此操作,但它看起来相当繁琐!

double[,,,] arrayToFill = new double[7, 8, 9, 10];

var data = MemoryMarshal.CreateSpan(
    ref Unsafe.As<byte, double>(ref MemoryMarshal.GetArrayDataReference(arrayToFill)),
    arrayToFill.Length);

data.Fill(1.2345);

foreach (var value in arrayToFill)
{
    Console.WriteLine(value);  // All values are 1.2345
}
Run Code Online (Sandbox Code Playgroud)

您可以编写一个方法来封装它:

public static void FillArray<T>(Array array, T value)
{
    var data = MemoryMarshal.CreateSpan(
        ref Unsafe.As<byte, T>(ref MemoryMarshal.GetArrayDataReference(array)),
        array.Length);

    data.Fill(value);
}
Run Code Online (Sandbox Code Playgroud)

然后调用站点变得更具可读性:

double[,,,] arrayToFill = new double[7, 8, 9, 10];

FillArray(arrayToFill, 1.2345);

foreach (var value in arrayToFill)
{
    Console.WriteLine(value);  // All values are 1.2345
}
Run Code Online (Sandbox Code Playgroud)

如果你想变得更奇特,你可以将其封装在扩展方法中:

public static class ArrayExt
{
    public static void Fill<T>(this Array array, T value)
    {
        var data = MemoryMarshal.CreateSpan(
            ref Unsafe.As<byte, T>(ref MemoryMarshal.GetArrayDataReference(array)),
            array.Length);

        data.Fill(value);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你可以像这样调用它:

double[,,,] arrayToFill = new double[7, 8, 9, 10];

arrayToFill.Fill(1.2345);

foreach (var value in arrayToFill)
{
    Console.WriteLine(value);  // All values are 1.2345
}
Run Code Online (Sandbox Code Playgroud)

重要的!您必须确保使用正确的类型来填充数组。如果指定了错误的类型,它将用垃圾填充数组,例如:

arrayToFill.Fill(1);
Run Code Online (Sandbox Code Playgroud)

真的会把事情搞砸的。

在此示例中,您需要arrayToFill.Fill<double>(1);指定正确的类型,否则它将推断出填充数组的错误类型。

根据 /u/charlieface 的评论,您可以通过为每个数组维度添加强类型重载来规避此问题,因此:

这可能是最好的方法:

public static class ArrayExt
{
    public static void Fill<T>(this T[,]    array, T value) => fill(array, value);
    public static void Fill<T>(this T[,,]   array, T value) => fill(array, value);
    public static void Fill<T>(this T[,,,]  array, T value) => fill(array, value);
    public static void Fill<T>(this T[,,,,] array, T value) => fill(array, value);

    static void fill<T>(Array array, T value)
    {
        var data = MemoryMarshal.CreateSpan(
            ref Unsafe.As<byte, T>(ref MemoryMarshal.GetArrayDataReference(array)),
            array.Length);

        data.Fill(value);
    }

    // ...etc etc. But does anyone really need more than a 5D array? ;)
}
Run Code Online (Sandbox Code Playgroud)

然后arrayToFill.Fill(1);就可以正常工作了。


另请参阅/u/charlieface 的这篇文章