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
相关产品推荐
相关产品推荐

