无需复制即可将C#double []数组转换为double []

时间:2020-03-06 14:23:06  来源:igfitidea点击:

我的.NET应用程序中有大量3D数字数组。我需要将它们转换为一维数组以将其传递到COM库。有没有一种方法可以在不复制所有数据的情况下转换数组?

我可以这样进行转换,但是然后我使用了两倍的内存量,这在我的应用程序中是一个问题:

double[] result = new double[input.GetLength(0) * input.GetLength(1) * input.GetLength(2)];
  for (i = 0; i < input.GetLength(0); i++) 
    for (j = 0; j < input.GetLength(1); j++) 
      for (k = 0; k < input.GetLength(2); k++) 
        result[i * input.GetLength(1) * input.GetLength(2) + j * input.GetLength(2) + k)] = input[i,j,l];
  return result;

解决方案

不幸的是,不能保证Carrays处于连续的内存中,就像它们是像C这样最接近金属的语言一样。如果没有逐元素复制,则无法将double []转换为double []。

我不相信C将数据存储在内存中的方式使它像在C中进行简单转换一样可行。为什么不使用1d数组开头并为该类型创建一个类,以便我们可以在程序中访问它,就好像它是3d数组一样?

在不了解COM库详细信息的情况下,我将考虑在.Net中创建一个Facade类,并在必要时将其公开给COM。
外观将采用double [],并有一个索引器,该索引器将从[]映射到[]。

编辑:我同意评论中的观点,洛伦斯的建议更好。

解决方法是,创建一个以一维形式维护数组的类(甚至更接近裸机形式,以便可以轻松地将其传递给COM库?),然后在此类上重载operator []以使其可用作为Ccode中的多维数组。

考虑使用代理来抽象化对数据的访问(类似于C ++中的迭代器/智能指针)。不幸的是,语法不像C ++那样干净,因为operator()无法重载,operator []是单参数,但仍然很接近。

当然,这种额外的抽象级别增加了它自己的复杂性和工作量,但是它允许我们对使用double []对象的现有代码进行最小的更改,同时允许我们对单个对象使用单个double []数组互操作性和计算能力。

class Matrix3
{
    // referece-to-element object
    public struct Matrix3Elem{
        private Matrix3Impl impl;
        private uint dim0, dim1, dim2;
        // other constructors
        Matrix3Elem(Matrix3Impl impl_, uint dim0_, uint dim1_, uint dim2_) {
            impl = impl_; dim0 = dim0_; dim1 = dim1_; dim2 = dim2_;
        }
        public double Value{
            get { return impl.GetAt(dim0,dim1,dim2); }
            set { impl.SetAt(dim0, dim1, dim2, value); }
        }
    }

    // implementation object
    internal class Matrix3Impl
    {
        private double[] data;
        uint dsize0, dsize1, dsize2; // dimension sizes
        // .. Resize() 
        public double GetAt(uint dim0, uint dim1, uint dim2) {
            // .. check bounds
            return data[ (dim2 * dsize1 + dim1) * dsize0 + dim0 ];
        }
        public void SetAt(uint dim0, uint dim1, uint dim2, double value) {
            // .. check bounds
            data[ (dim2 * dsize1 + dim1) * dsize0 + dim0 ] = value;
        }
    }

    private Matrix3Impl impl;

    public Matrix3Elem Elem(uint dim0, uint dim1, uint dim2){
        return new Matrix2Elem(dim0, dim1, dim2);
    }
    // .. Resize
    // .. GetLength0(), GetLength1(), GetLength1()
}

然后使用这种类型进行读取和写入-在左侧读取值和写入值时,'foo [1,2,3]'现在被写为'foo.Elem(1,2,3).Value'赋值和值表达式的一面。

void normalize(Matrix3 m){

    double s = 0;
    for (i = 0; i < input.GetLength0; i++)     
        for (j = 0; j < input.GetLength(1); j++)       
            for (k = 0; k < input.GetLength(2); k++)
    {
        s += m.Elem(i,j,k).Value;
    }
    for (i = 0; i < input.GetLength0; i++)     
        for (j = 0; j < input.GetLength(1); j++)       
            for (k = 0; k < input.GetLength(2); k++)
    {
        m.Elem(i,j,k).Value /= s;
    }
}

同样,增加了开发成本,但共享了数据,从而消除了复制开销和复制相关的开发成本。这是一个权衡。