You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#中如何不复制数据将一维数组转换为多维数组?

在C#中无需复制数据将超大一维数组重解释为二维数组

以下是几种不复制数据实现需求的方法(均为不安全操作,仅在极端场景下使用):

方法1:通过Unsafe模拟数组结构替换数据指针

该方法直接修改二维数组的内部数据指针,使其指向一维数组的内存区域,依赖CLR的数组内部内存布局(仅适配x64的.NET Core/.NET 5+环境):

using System.Runtime.CompilerServices;

public static unsafe double[,] ReinterpretAs2D(double[] vals, int rows, int cols)
{
    if (vals.Length != rows * cols)
        throw new System.ArgumentException("一维数组长度与二维数组元素总数不匹配");

    // 创建目标二维数组(临时分配的内存后续会被GC回收)
    double[,] output = new double[rows, cols];

    // 模拟一维数组的内部结构(x64环境)
    struct SingleDimensionalArray
    {
        public System.IntPtr MethodTable;
        public int Length;
        public double FirstElement;
    }

    // 模拟二维数组的内部结构(x64环境)
    struct MultiDimensionalArray
    {
        public System.IntPtr MethodTable;
        public int Rank;
        public int Length0;
        public int Length1;
        public int LowerBound0;
        public int LowerBound1;
        public double FirstElement;
    }

    // 将数组转换为结构体视图,替换数据指针
    var singleArray = Unsafe.As<double[], SingleDimensionalArray>(vals);
    var multiArray = Unsafe.As<double[,], MultiDimensionalArray>(output);
    multiArray.FirstElement = singleArray.FirstElement;

    return output;
}

方法2:使用Span创建二维视图(非数组类型)

若无需严格返回double[,]类型,仅需要二维访问能力,可通过Span实现零复制:

using System;
using System.Runtime.InteropServices;

public static Span<double, 2> As2DSpan(double[] vals, int rows, int cols)
{
    if (vals.Length != rows * cols)
        throw new ArgumentException("一维数组长度与二维数组元素总数不匹配");
    
    return MemoryMarshal.CreateSpan<double, 2>(ref vals[0], rows, cols);
}

使用时直接通过span[row, col]访问元素,无需复制数据,但不能直接转换为double[,]数组。

方法3:C++/CLI辅助实现(兼容性更好)

若允许引入C++/CLI项目,可直接操作托管数组的内部指针,适配更多CLR版本:

#include <msclr/marshal.h>
using namespace System;
using namespace System::Runtime::InteropServices;

public ref class ArrayConverter
{
public:
    static cli::array<double, 2>^ ReinterpretTo2D(cli::array<double>^ vals, int rows, int cols)
    {
        if (vals->Length != rows * cols)
            throw gcnew ArgumentException("一维数组长度与二维数组元素总数不匹配");

        cli::array<double, 2>^ output = gcnew cli::array<double, 2>(rows, cols);
        
        // 获取一维数组的数据起始地址
        pin_ptr<double> valsPtr = &vals[0];
        IntPtr valsDataAddr = IntPtr(valsPtr);

        // 获取二维数组对象的内存地址
        GCHandle outputHandle = GCHandle::Alloc(output, GCHandleType::Pinned);
        IntPtr outputObjAddr = outputHandle.AddrOfPinnedObject();
        outputHandle.Free();

        // 替换二维数组的数据指针(x64环境下偏移为24字节)
        IntPtr dataFieldAddr = IntPtr((long long)outputObjAddr + 3 * sizeof(void*));
        Marshal::WriteIntPtr(dataFieldAddr, valsDataAddr);

        return output;
    }
};

在C#中直接调用ArrayConverter.ReinterpretTo2D(vals, 1000, 1000000)即可。


内容的提问来源于stack exchange,提问作者w13245

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 05:17:38