【问题标题】:How can you resize a 1-D array to a 2-D array in C# without copying?如何在不复制的情况下在 C# 中将一维数组的大小调整为二维数组?
【发布时间】:2017-10-27 18:07:45
【问题描述】:

据我所知,二维数组在内存中的布局与一维数组完全相同,只是使用不同的索引。因此,假设您希望将一维数组中的元素转换为二维数组以行优先的方式。我确实想出了如何使用单个复制命令来完成此操作,但它似乎仍然需要更多的处理:

int[] onedim = new int[] { 5, 3, 6, 2, 1, 5 };
int[,] twodim = new int[2, 3];
GCHandle pinnedArray = GCHandle.Alloc(twodim, GCHandleType.Pinned);
IntPtr pointer = pinnedArray.AddrOfPinnedObject();
Marshal.Copy(onedim, 0, pointer, onedim.Length);
pinnedArray.Free();

有什么方法可以告诉 C#,将这个一维数组视为具有这些维度的二维数组吗?不安全的代码很好。如果不是,这是语言的限制还是我遗漏了一些基本问题?

【问题讨论】:

  • 数组转换为二维数组后如何使用?
  • @AaronRoberts 不确定你的意思。它的行为应该与您声明二维数组内联时完全相同,例如int[,] twodim = new int[,] { { 5, 3, 6 }, { 2, 1, 5 } }.
  • 您是在创建初始 int[] 还是其他人?
  • @Moop 别人。它是从 C DLL 传递给我的。
  • 你能显示提供数组的外部方法的签名吗?

标签: c# .net arrays


【解决方案1】:

一种方法是定义一个访问底层一维数组的类

public class Array2D<T>
{
    private readonly T[] _data;
    private readonly Int32 _dimX;
    private readonly Int32 _dimY;

    public Array2D(T[] data, Int32 dimX, Int32 dimY)
    {
        _data = data;
        _dimX = dimX;
        _dimY = dimY;
    }

    public T this [Int32 x, Int32 y]
    {
        get { return _data[x * _dimY + y]; }
        set { _data[x * _dimY + y] = value; }
    }
}

var array = new[] { 1, 2, 3, 4, 5, 6 };
var array2d = new Array2D<Int32>(array, 2, 3);

Assert.AreEqual(array2d[0, 0], array[0]);
Assert.AreEqual(array2d[0, 1], array[1]);
Assert.AreEqual(array2d[0, 2], array[2]);
Assert.AreEqual(array2d[1, 0], array[3]);
Assert.AreEqual(array2d[1, 1], array[4]);
Assert.AreEqual(array2d[1, 2], array[5]);

我的索引可能是向后的,但是这会表现得像一个二维数组,同时保持一维数组引用。

编辑:索引是向后的,修复它。

【讨论】:

    【解决方案2】:

    您可以尝试改用Buffer.BlockCopy 方法:

    Buffer.BlockCopy(onedim, 0, twodim, 0, onedim.Length * sizeof(int));
    

    它也接受多维数组。

    【讨论】:

    • 块拷贝仍然是拷贝。
    • 虽然它仍然会复制,但这肯定很有帮助,因为它比我想出的要简单得多。
    • @Mitch 当然!只有两个选项:复制或使用具有二维索引器的类包装。每个人都有自己的优点和缺点。但是只有副本会给出一个二维数组,它可以作为参数传递给接受例如T[,]的方法。
    • 是的,但问题是“如何在 C# 中将一维数组的大小调整为二维数组而不进行复制?”。我并不是说您的回答没有帮助,而是说它没有回答所问的问题。您可以考虑添加警告“我不知道有任何方法可以在不复制的情况下这样做,但是......”
    【解决方案3】:

    使用Marshal.PtrToStructure

    如果不将二维数组包装在结构中,我无法弄清楚如何做到这一点,但以下内容无需副本即可工作。

        struct array2d 
        {
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
            int[,] data;
        };
    
    
        static void Main(string[] args)
        {
    
            int[] onedim = new int[] { 5, 3, 6, 2, 1, 5 };
    
            array2d twoDim;
    
            unsafe
            {
                fixed (int* p = onedim)
                {
                    twoDim = Marshal.PtrToStructure<array2d>(new IntPtr(p));
                }
            }
    
        }
    

    【讨论】:

    • 如果一维数组的大小不是编译时常量怎么办?在您的示例中,array2d.data: [1,6]、[2,3]、[3,2]、[6,1] 的尺寸是多少?
    • @Dmitry 奇怪的是,在调试器中查看时,array2d.data 是一维数组。虽然编译器允许访问twoDim.data[0,0],但我得到一个索引越界错误。
    • PtrToStructure 仍然执行复制。
    • @Mitch 它并没有说它在文档中复制。你有参考吗?
    • 您正在传递一个非托管指针,它正在返回一个托管结构,这需要一个副本。 From MSDN "以下示例创建一个托管结构,将其传输到非托管内存,然后使用 PtrToStructure 方法将其传输回托管内存。"
    猜你喜欢
    • 2017-01-05
    • 2019-04-14
    • 2011-02-08
    • 1970-01-01
    • 1970-01-01
    • 2020-11-15
    • 2020-12-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多